<commit_before>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package codec

import (
	"bytes"
	"runtime"
	"unsafe"

	"github.com/juju/errors"
)

// EncodeBytes encodes the slice value using an escape based encoding,
// with the following rule:
//  \x00 -> \x00\xFF
//  \xFF -> \xFF\x00 if the first byte is \xFF
// EncodeBytes will append \x00\x01 at the end of the encoded value to
// indicate the termination.
// EncodeBytes guarantees the encoded value is in ascending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytes(b []byte, data []byte) []byte {
	if len(data) > 0 && data[0] == 0xFF {
		// we must escape 0xFF here to guarantee encoded value < InfiniteValue \xFF\xFF.
		b = append(b, 0xFF, 0x00)
		data = data[1:]
	}

	for {
		// find 0x00 and escape it.
		i := bytes.IndexByte(data, 0x00)
		if i == -1 {
			break
		}
		b = append(b, data[:i]...)
		b = append(b, 0x00, 0xFF)
		data = data[i+1:]
	}
	b = append(b, data...)
	return append(b, 0x00, 0x01)
}

// DecodeBytes decodes bytes which is encoded by EncodeBytes before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytes(b []byte) ([]byte, []byte, error) {
	return decodeBytes(b, 0xFF, 0x00, 0x01)
}

func decodeBytes(b []byte, escapeFirst byte, escape byte, term byte) ([]byte, []byte, error) {
	if len(b) < 2 {
		return nil, nil, errors.Errorf("insufficient bytes to decode value")
	}

	var r []byte

	if b[0] == escapeFirst {
		if b[1] != ^escapeFirst {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but 0x%x", ^escapeFirst, b[1])
		}
		r = append(r, escapeFirst)
		b = b[2:]
	}

	for {
		i := bytes.IndexByte(b, escape)
		if i == -1 {
			return nil, nil, errors.Errorf("invalid termination in bytes")
		}

		if i+1 >= len(b) {
			return nil, nil, errors.Errorf("malformed escaped bytes")
		}

		if b[i+1] == term {
			// For `var r []byte`, `b := []byte{}`, `r = append(r, b...)`,
			// here r is equals to nil, so it is better to distinguish it.
			if r == nil {
				r = b[:i]
			} else {
				r = append(r, b[:i]...)
			}

			return b[i+2:], r, nil
		}

		if b[i+1] != ^escape {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but got 0x%0x", ^escape, b[i+1])
		}

		r = append(r, b[:i]...)
		r = append(r, escape)
		b = b[i+2:]
	}
}

// EncodeBytesDesc first encodes bytes using EncodeBytes, then bitwise reverses
// encoded value to guarentee the encoded value is in descending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytesDesc(b []byte, data []byte) []byte {
	n := len(b)
	b = EncodeBytes(b, data)
	reverseBytes(b[n:])
	return b
}

// DecodeBytesDesc decodes bytes which is encoded by EncodeBytesDesc before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytesDesc(b []byte) ([]byte, []byte, error) {
	var (
		r   []byte
		err error
	)
	b, r, err = decodeBytes(b, 0x00, 0xFF, ^byte(0x01))
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	reverseBytes(r)
	return b, r, nil
}

// See https://golang.org/src/crypto/cipher/xor.go
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"

func fastReverseBytes(b []byte) {
	n := len(b)
	w := n / wordSize
	if w > 0 {
		bw := *(*[]uintptr)(unsafe.Pointer(&b))
		for i := 0; i < w; i++ {
			bw[i] = ^bw[i]
		}
	}

	for i := w * wordSize; i < n; i++ {
		b[i] = ^b[i]
	}
}

func safeReverseBytes(b []byte) {
	for i := range b {
		b[i] = ^b[i]
	}
}

func reverseBytes(b []byte) {
	if supportsUnaligned {
		fastReverseBytes(b)
		return
	}

	safeReverseBytes(b)
}
<commit_msg>util: address comment.<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package codec

import (
	"bytes"
	"runtime"
	"unsafe"

	"github.com/juju/errors"
)

// EncodeBytes encodes the slice value using an escape based encoding,
// with the following rule:
//  \x00 -> \x00\xFF
//  \xFF -> \xFF\x00 if the first byte is \xFF
// EncodeBytes will append \x00\x01 at the end of the encoded value to
// indicate the termination.
// EncodeBytes guarantees the encoded value is in ascending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytes(b []byte, data []byte) []byte {
	if len(data) > 0 && data[0] == 0xFF {
		// we must escape 0xFF here to guarantee encoded value < InfiniteValue \xFF\xFF.
		b = append(b, 0xFF, 0x00)
		data = data[1:]
	}

	for {
		// find 0x00 and escape it.
		i := bytes.IndexByte(data, 0x00)
		if i == -1 {
			break
		}
		b = append(b, data[:i]...)
		b = append(b, 0x00, 0xFF)
		data = data[i+1:]
	}
	b = append(b, data...)
	return append(b, 0x00, 0x01)
}

// DecodeBytes decodes bytes which is encoded by EncodeBytes before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytes(b []byte) ([]byte, []byte, error) {
	return decodeBytes(b, 0xFF, 0x00, 0x01)
}

func decodeBytes(b []byte, escapeFirst byte, escape byte, term byte) ([]byte, []byte, error) {
	if len(b) < 2 {
		return nil, nil, errors.Errorf("insufficient bytes to decode value")
	}

	var r []byte

	if b[0] == escapeFirst {
		if b[1] != ^escapeFirst {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but 0x%x", ^escapeFirst, b[1])
		}
		r = append(r, escapeFirst)
		b = b[2:]
	}

	for {
		i := bytes.IndexByte(b, escape)
		if i == -1 {
			return nil, nil, errors.Errorf("invalid termination in bytes")
		}

		if i+1 >= len(b) {
			return nil, nil, errors.Errorf("malformed escaped bytes")
		}

		if b[i+1] == term {
			if r == nil {
				r = b[:i]
			} else {
				r = append(r, b[:i]...)
			}

			return b[i+2:], r, nil
		}

		if b[i+1] != ^escape {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but got 0x%0x", ^escape, b[i+1])
		}

		r = append(r, b[:i]...)
		r = append(r, escape)
		b = b[i+2:]
	}
}

// EncodeBytesDesc first encodes bytes using EncodeBytes, then bitwise reverses
// encoded value to guarentee the encoded value is in descending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytesDesc(b []byte, data []byte) []byte {
	n := len(b)
	b = EncodeBytes(b, data)
	reverseBytes(b[n:])
	return b
}

// DecodeBytesDesc decodes bytes which is encoded by EncodeBytesDesc before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytesDesc(b []byte) ([]byte, []byte, error) {
	var (
		r   []byte
		err error
	)
	b, r, err = decodeBytes(b, 0x00, 0xFF, ^byte(0x01))
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	reverseBytes(r)
	return b, r, nil
}

// See https://golang.org/src/crypto/cipher/xor.go
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"

func fastReverseBytes(b []byte) {
	n := len(b)
	w := n / wordSize
	if w > 0 {
		bw := *(*[]uintptr)(unsafe.Pointer(&b))
		for i := 0; i < w; i++ {
			bw[i] = ^bw[i]
		}
	}

	for i := w * wordSize; i < n; i++ {
		b[i] = ^b[i]
	}
}

func safeReverseBytes(b []byte) {
	for i := range b {
		b[i] = ^b[i]
	}
}

func reverseBytes(b []byte) {
	if supportsUnaligned {
		fastReverseBytes(b)
		return
	}

	safeReverseBytes(b)
}
<|endoftext|>


<commit_before>package program

// Version can be requested through the command line with:
//
//     c2go -v
//
// See https://github.com/elliotchance/c2go/wiki/Release-Process
const Version = "v0.24.4 Cadmium 2018-06-04"
<commit_msg>Bump version: v0.24.5 Cadmium 2018-06-17<commit_after>package program

// Version can be requested through the command line with:
//
//     c2go -v
//
// See https://github.com/elliotchance/c2go/wiki/Release-Process
const Version = "v0.24.5 Cadmium 2018-06-17"
<|endoftext|>


<commit_before>package progress

import (
	"fmt"
	"strings"
	"time"
)

type Units int

const (
	// By default, without type handle
	U_NO Units = iota
	// Handle as b, Kb, Mb, etc
	U_BYTES
)

// Format integer
func Format(i int64, units Units, width int) string {
	switch units {
	case U_BYTES:
		return FormatBytes(i)
	default:
		// by default just convert to string
		return fmt.Sprintf(fmt.Sprintf("%%%dd", width), i)
	}
}

// Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B
func FormatBytes(i int64) (result string) {
	switch {
	case i > (1024 * 1024 * 1024 * 1024):
		result = fmt.Sprintf("%.02f TiB", float64(i)/1024/1024/1024/1024)
	case i > (1024 * 1024 * 1024):
		result = fmt.Sprintf("%.02f GiB", float64(i)/1024/1024/1024)
	case i > (1024 * 1024):
		result = fmt.Sprintf("%.02f MiB", float64(i)/1024/1024)
	case i > 1024:
		result = fmt.Sprintf("%.02f KiB", float64(i)/1024)
	default:
		result = fmt.Sprintf("%d B", i)
	}
	result = strings.Trim(result, " ")
	return
}

func FormatDuration(d time.Duration) string {
	res := ""
	if d > time.Hour*24 {
		res = fmt.Sprintf("%dd", d/24/time.Hour)
		d -= (d / time.Hour / 24) * (time.Hour * 24)
	}
	return fmt.Sprintf("%s%v ", res, d)
}
<commit_msg>fix comments<commit_after>package progress

import (
	"fmt"
	"strings"
	"time"
)

type Units int

const (
	// By default, without type handle
	U_NO Units = iota
	// Handle as b, Kb, Mb, etc
	U_BYTES
)

// Format integer
func Format(i int64, units Units, width int) string {
	switch units {
	case U_BYTES:
		return FormatBytes(i)
	default:
		// by default just convert to string
		return fmt.Sprintf(fmt.Sprintf("%%%dd", width), i)
	}
}

// Convert bytes to human readable string. Like a 2 MiB, 64.2 KiB, 52 B
func FormatBytes(i int64) (result string) {
	switch {
	case i > (1024 * 1024 * 1024 * 1024):
		result = fmt.Sprintf("%.02f TiB", float64(i)/1024/1024/1024/1024)
	case i > (1024 * 1024 * 1024):
		result = fmt.Sprintf("%.02f GiB", float64(i)/1024/1024/1024)
	case i > (1024 * 1024):
		result = fmt.Sprintf("%.02f MiB", float64(i)/1024/1024)
	case i > 1024:
		result = fmt.Sprintf("%.02f KiB", float64(i)/1024)
	default:
		result = fmt.Sprintf("%d B", i)
	}
	result = strings.Trim(result, " ")
	return
}

func FormatDuration(d time.Duration) string {
	res := ""
	if d > time.Hour*24 {
		res = fmt.Sprintf("%dd", d/24/time.Hour)
		d -= (d / time.Hour / 24) * (time.Hour * 24)
	}
	return fmt.Sprintf("%s%v ", res, d)
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"io"
	"strings"
)

// ConfirmArgs encapsulates the arguments that can he passed to GetConfirm
type ConfirmArgs struct {
	Out     io.Writer // os.Stdout or &bytes.Buffer used to putput the message above the confirmation
	Message string    // what you want to say to the user before confirming
	Default string    // if you hit enter instead of yes or no shoudl it approve or deny
	TestVal string    // if you need to test without the interactive prompt then set the user response here
}

// GetConfirm prompts a user for a yes or no answer.
// In order to test this function som extra parameters are reqired:
//
// out: an io.Writer that allows you to direct prints to stdout or another location
// message: the string that will be printed just before prompting for a yes or no.
// answer: "", "yes", or "no" - this allows for easier testing
func GetConfirm(c *ConfirmArgs) bool {
	if c.Default != "" {
		c.Default = strings.ToLower(c.Default)
	}
	answerTemplate := "(%s/%s)"
	switch c.Default {
	case "yes", "y":
		c.Message = c.Message + fmt.Sprintf(answerTemplate, "Y", "n")
	case "no", "n":
		c.Message = c.Message + fmt.Sprintf(answerTemplate, "y", "N")
	default:
		c.Message = c.Message + fmt.Sprintf(answerTemplate, "y", "n")
	}
	fmt.Fprintln(c.Out, c.Message)

	// these are the acceptable answers
	okayResponses := []string{"y", "yes"}
	nokayResponses := []string{"n", "no"}
	response := c.TestVal

	// only prompt user if you predefined answer was passed in
	if response == "" {
		_, err := fmt.Scanln(&response)
		if err != nil {
			return false
		}
	}

	responseLower := strings.ToLower(response)
	// make sure the response is valid
	if ContainsString(okayResponses, responseLower) {
		return true
	} else if ContainsString(nokayResponses, responseLower) {
		return false
	} else if c.Default != "" {
		if string(c.Default[0]) == "y" {
			return true
		}
		return false
	}
	return GetConfirm(c)
}

// ContainsString returns true if slice contains the element
func ContainsString(slice []string, element string) bool {
	return !(strings.Index(strings.Join(slice, " "), element) == -1)
}
<commit_msg>updating header<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package ui

import (
	"fmt"
	"io"
	"strings"
)

// ConfirmArgs encapsulates the arguments that can he passed to GetConfirm
type ConfirmArgs struct {
	Out     io.Writer // os.Stdout or &bytes.Buffer used to putput the message above the confirmation
	Message string    // what you want to say to the user before confirming
	Default string    // if you hit enter instead of yes or no shoudl it approve or deny
	TestVal string    // if you need to test without the interactive prompt then set the user response here
}

// GetConfirm prompts a user for a yes or no answer.
// In order to test this function som extra parameters are reqired:
//
// out: an io.Writer that allows you to direct prints to stdout or another location
// message: the string that will be printed just before prompting for a yes or no.
// answer: "", "yes", or "no" - this allows for easier testing
func GetConfirm(c *ConfirmArgs) bool {
	if c.Default != "" {
		c.Default = strings.ToLower(c.Default)
	}
	answerTemplate := "(%s/%s)"
	switch c.Default {
	case "yes", "y":
		c.Message = c.Message + fmt.Sprintf(answerTemplate, "Y", "n")
	case "no", "n":
		c.Message = c.Message + fmt.Sprintf(answerTemplate, "y", "N")
	default:
		c.Message = c.Message + fmt.Sprintf(answerTemplate, "y", "n")
	}
	fmt.Fprintln(c.Out, c.Message)

	// these are the acceptable answers
	okayResponses := []string{"y", "yes"}
	nokayResponses := []string{"n", "no"}
	response := c.TestVal

	// only prompt user if you predefined answer was passed in
	if response == "" {
		_, err := fmt.Scanln(&response)
		if err != nil {
			return false
		}
	}

	responseLower := strings.ToLower(response)
	// make sure the response is valid
	if ContainsString(okayResponses, responseLower) {
		return true
	} else if ContainsString(nokayResponses, responseLower) {
		return false
	} else if c.Default != "" {
		if string(c.Default[0]) == "y" {
			return true
		}
		return false
	}
	return GetConfirm(c)
}

// ContainsString returns true if slice contains the element
func ContainsString(slice []string, element string) bool {
	return !(strings.Index(strings.Join(slice, " "), element) == -1)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package vfs

import (
	"fmt"
	"io"
	"strings"

	"k8s.io/klog"
	"k8s.io/kops/util/pkg/hashing"
)

// Yet another VFS package
// If there's a "winning" VFS implementation in go, we should switch to it!

type VFS interface {
}

func IsDirectory(p Path) bool {
	_, err := p.ReadDir()
	return err == nil
}

type ACL interface {
}

type ACLOracle func(Path) (ACL, error)

// Path is a path in the VFS space, which we can read, write, list etc
type Path interface {
	io.WriterTo

	Join(relativePath ...string) Path

	// ReadFile returns the contents of the file, or an error if the file could not be read.
	// If the file did not exist, err = os.ErrNotExist
	// As this reads the entire file into memory, consider using WriteTo for bigger files
	ReadFile() ([]byte, error)

	WriteFile(data io.ReadSeeker, acl ACL) error
	// CreateFile writes the file contents, but only if the file does not already exist
	CreateFile(data io.ReadSeeker, acl ACL) error

	// Remove deletes the file
	Remove() error

	// Base returns the base name (last element)
	Base() string

	// Path returns a string representing the full path
	Path() string

	// ReadDir lists the files in a particular Path
	ReadDir() ([]Path, error)

	// ReadTree lists all files (recursively) in the subtree rooted at the current Path
	/// Note: returns only files, not directories
	ReadTree() ([]Path, error)
}

type HasHash interface {
	// Returns the hash of the file contents, with the preferred hash algorithm
	PreferredHash() (*hashing.Hash, error)

	// Gets the hash, or nil if the hash cannot be (easily) computed
	Hash(algorithm hashing.HashAlgorithm) (*hashing.Hash, error)
}

func RelativePath(base Path, child Path) (string, error) {
	basePath := base.Path()
	childPath := child.Path()
	if !strings.HasSuffix(basePath, "/") {
		basePath += "/"
	}

	if !strings.HasPrefix(childPath, basePath) {
		return "", fmt.Errorf("Path %q is not a child of %q", child, base)
	}

	relativePath := childPath[len(basePath):]
	return relativePath, nil
}

func IsClusterReadable(p Path) bool {
	if hcr, ok := p.(HasClusterReadable); ok {
		return hcr.IsClusterReadable()
	}

	switch p.(type) {
	case *S3Path, *GSPath, *SwiftPath, *OSSPath:
		return true

	case *KubernetesPath:
		return true

	case *SSHPath:
		return false

	case *FSPath:
		return false

	case *MemFSPath:
		return false

	default:
		klog.Fatalf("IsClusterReadable not implemented for type %T", p)
		return false
	}
}

type HasClusterReadable interface {
	IsClusterReadable() bool
}
<commit_msg>Allow local filesystem state stores<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package vfs

import (
	"fmt"
	"io"
	"strings"

	"k8s.io/klog"
	"k8s.io/kops/util/pkg/hashing"
)

// Yet another VFS package
// If there's a "winning" VFS implementation in go, we should switch to it!

type VFS interface {
}

func IsDirectory(p Path) bool {
	_, err := p.ReadDir()
	return err == nil
}

type ACL interface {
}

type ACLOracle func(Path) (ACL, error)

// Path is a path in the VFS space, which we can read, write, list etc
type Path interface {
	io.WriterTo

	Join(relativePath ...string) Path

	// ReadFile returns the contents of the file, or an error if the file could not be read.
	// If the file did not exist, err = os.ErrNotExist
	// As this reads the entire file into memory, consider using WriteTo for bigger files
	ReadFile() ([]byte, error)

	WriteFile(data io.ReadSeeker, acl ACL) error
	// CreateFile writes the file contents, but only if the file does not already exist
	CreateFile(data io.ReadSeeker, acl ACL) error

	// Remove deletes the file
	Remove() error

	// Base returns the base name (last element)
	Base() string

	// Path returns a string representing the full path
	Path() string

	// ReadDir lists the files in a particular Path
	ReadDir() ([]Path, error)

	// ReadTree lists all files (recursively) in the subtree rooted at the current Path
	/// Note: returns only files, not directories
	ReadTree() ([]Path, error)
}

type HasHash interface {
	// Returns the hash of the file contents, with the preferred hash algorithm
	PreferredHash() (*hashing.Hash, error)

	// Gets the hash, or nil if the hash cannot be (easily) computed
	Hash(algorithm hashing.HashAlgorithm) (*hashing.Hash, error)
}

func RelativePath(base Path, child Path) (string, error) {
	basePath := base.Path()
	childPath := child.Path()
	if !strings.HasSuffix(basePath, "/") {
		basePath += "/"
	}

	if !strings.HasPrefix(childPath, basePath) {
		return "", fmt.Errorf("Path %q is not a child of %q", child, base)
	}

	relativePath := childPath[len(basePath):]
	return relativePath, nil
}

func IsClusterReadable(p Path) bool {
	if hcr, ok := p.(HasClusterReadable); ok {
		return hcr.IsClusterReadable()
	}

	switch p.(type) {
	case *S3Path, *GSPath, *SwiftPath, *OSSPath, *FSPath:
		return true

	case *KubernetesPath:
		return true

	case *SSHPath:
		return false

	case *MemFSPath:
		return false

	default:
		klog.Fatalf("IsClusterReadable not implemented for type %T", p)
		return false
	}
}

type HasClusterReadable interface {
	IsClusterReadable() bool
}
<|endoftext|>


<commit_before>package utils

import (
	"testing"
	"time"
)

func BenchmarkParseDate(t *testing.B) {
	var strdate = "104/04/01"
	for i := 0; i < t.N; i++ {
		ParseDate(strdate)
	}
}

func BenchmarkSumUint64(t *testing.B) {
	var sample = []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9}
	for i := 0; i < t.N; i++ {
		SumUint64(sample)
	}
}

func BenchmarkAvgUint64(t *testing.B) {
	var sample = []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9}
	for i := 0; i < t.N; i++ {
		AvgUint64(sample)
	}
}

func BenchmarkRanInt(t *testing.B) {
	for i := 0; i < t.N; i++ {
		RandInt()
	}
}

func BenchmarkSumFloat64(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		SumFloat64(sample)
	}
}

func BenchmarkAvgFloat64(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		AvgFloat64(sample)
	}
}

func BenchmarkThanPast(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		thanPast(sample, true)
	}
}

func BenchmarkThanPast_false(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		thanPast(sample, false)
	}
}

func BenchmarkThanPastFloat64(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		ThanPastFloat64(sample, 3, true)
	}
}

func BenchmarkThanPastUint64(t *testing.B) {
	var sample = []uint64{20, 23, 125, 105}
	for i := 0; i < t.N; i++ {
		ThanPastUint64(sample, 3, true)
	}
}

func BenchmarkThanSumPastUint64(b *testing.B) {
	var sample = []uint64{10, 11, 12, 53}
	for i := 0; i < b.N; i++ {
		ThanSumPastUint64(sample, 3, true)
	}
}

func BenchmarkThanSumPast(b *testing.B) {
	var sample = []float64{10.1, 11.1, 12.1, 53.1}
	for i := 0; i < b.N; i++ {
		thanSumPast(sample, true)
	}
}

func TestRanInt(t *testing.T) {
	if (RandInt() - RandInt() + RandInt() - RandInt()) == 0 {
		t.Error("Should not be the same.")
	}
}

func TestParseDate(t *testing.T) {
	var sample1 = "104/2/28"
	if ParseDate(sample1) != time.Date(2015, 2, 28, 0, 0, 0, 0, TaipeiTimeZone) {
		t.Error("Should be 2015/2/28")
	}
	var sample2 = "104/2/29"
	if ParseDate(sample2) != time.Date(2015, 3, 1, 0, 0, 0, 0, TaipeiTimeZone) {
		t.Error("Should be 2015/3/1")
	}
	var sample3 = "104/4/31"
	if ParseDate(sample3) != time.Date(2015, 5, 1, 0, 0, 0, 0, TaipeiTimeZone) {
		t.Error("Should be 2015/5/1")
	}
}

func TestAvg(t *testing.T) {
	var sample1 = []float64{3.3, 6.6, 9.9}
	if AvgFloat64(sample1) != 6.59 {
		t.Error("Should be 6.59")
	}
	var sample2 = []uint64{3, 6, 9}
	if AvgUint64(sample2) != 6 {
		t.Error("Should be 6")
	}
}

func TestSum(t *testing.T) {
	var sample1 = []float64{1.1, 2.2, 3.3}
	if SumFloat64(sample1) != 6.6 {
		t.Error("Should be 6.6")
	}
	var sample2 = []uint64{1, 2, 3}
	if SumUint64(sample2) != 6 {
		t.Error("Should be 6")
	}
}

func TestThanPast(t *testing.T) {
	var sample1 = []float64{10.1, 11.1, 12.1, 13.1}
	if !ThanPastFloat64(sample1, 3, true) {
		t.Error("Should be `true`")
	}
	var sample2 = []float64{10.1, 11.1, 12.1, 9.1}
	if !ThanPastFloat64(sample2, 3, false) {
		t.Error("Should be `true`")
	}
	if ThanPastFloat64(sample2, 3, true) {
		t.Error("Should be `false`")
	}
	var sample3 = []uint64{10, 11, 12, 13}
	if !ThanPastUint64(sample3, 3, true) {
		t.Error("Should be `true`")
	}
	var sample4 = []uint64{10, 11, 12, 9}
	if !ThanPastUint64(sample4, 3, false) {
		t.Error("Should be `true`")
	}
}

func TestThanSumPast(t *testing.T) {
	var sample1 = []float64{10.1, 11.1, 12.1, 53.1}
	if !ThanSumPastFloat64(sample1, 3, true) {
		t.Error("Should be `true`")
	}
	var sample2 = []float64{10.1, 11.1, 12.1, 10.1}
	if !ThanSumPastFloat64(sample2, 3, false) {
		t.Error("Should be `true`")
	}
	var sample3 = []uint64{10, 11, 12, 53}
	if !ThanSumPastUint64(sample3, 3, true) {
		t.Error("Should be `true`")
	}
	var sample4 = []uint64{10, 11, 12, 10}
	if !ThanSumPastUint64(sample4, 3, false) {
		t.Error("Should be `true`")
	}
}

func BenchmarkCountCountine(b *testing.B) {
	var sample1 = []float64{10.1, 11.1, 12.1, 53.1}
	for i := 0; i < b.N; i++ {
		CountCountineFloat64(sample1)
	}
}

func TestCountCountine(t *testing.T) {
	var sample1 = []float64{10.1, 11.1, 12.1, 53.1}  // 4 true
	var sample2 = []float64{10.1, 11.1, -12.1, 53.1} // 1 true
	var sample3 = []float64{10.1, 11.1, 12.1, -53.1} // 1 false
	if times, max := CountCountineFloat64(sample1); times != 4 && max != true {
		t.Error("Should be `4 true`")
	}
	if times, max := CountCountineFloat64(sample2); times != 1 && max != true {
		t.Error("Should be `1 true`")
	}
	if times, max := CountCountineFloat64(sample3); times != 1 && max != false {
		t.Error("Should be `1 false`")
	}
}

func TestCalDiff(t *testing.T) {
	var sampleA = []float64{10.0, 11.1, 12.2, 13.3}
	var sampleB = []float64{12.2, 11.1, 10.0}
	var result = CalDiffFloat(sampleA, sampleB)
	if result[2] != float64(13.3)-float64(10.0) {
		t.Error("Wrong cal.")
	}
}

func BenchmarkCalDiff(b *testing.B) {
	var sampleA = []float64{10.0, 11.1, 12.2, 13.3}
	var sampleB = []float64{12.2, 11.1, 10.0}
	for i := 0; i < b.N; i++ {
		CalDiffFloat(sampleA, sampleB)
	}
}
<commit_msg>Add some test.<commit_after>package utils

import (
	"testing"
	"time"
)

func BenchmarkParseDate(t *testing.B) {
	var strdate = "104/04/01"
	for i := 0; i < t.N; i++ {
		ParseDate(strdate)
	}
}

func BenchmarkSumUint64(t *testing.B) {
	var sample = []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9}
	for i := 0; i < t.N; i++ {
		SumUint64(sample)
	}
}

func BenchmarkAvgUint64(t *testing.B) {
	var sample = []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9}
	for i := 0; i < t.N; i++ {
		AvgUint64(sample)
	}
}

func BenchmarkRanInt(t *testing.B) {
	for i := 0; i < t.N; i++ {
		RandInt()
	}
}

func BenchmarkSumFloat64(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		SumFloat64(sample)
	}
}

func BenchmarkAvgFloat64(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		AvgFloat64(sample)
	}
}

func BenchmarkThanPast(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		thanPast(sample, true)
	}
}

func BenchmarkThanPast_false(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		thanPast(sample, false)
	}
}

func BenchmarkThanPastFloat64(t *testing.B) {
	var sample = []float64{20.2, 20.3, 100.25, 100.75}
	for i := 0; i < t.N; i++ {
		ThanPastFloat64(sample, 3, true)
	}
}

func BenchmarkThanPastUint64(t *testing.B) {
	var sample = []uint64{20, 23, 125, 105}
	for i := 0; i < t.N; i++ {
		ThanPastUint64(sample, 3, true)
	}
}

func BenchmarkThanSumPastUint64(b *testing.B) {
	var sample = []uint64{10, 11, 12, 53}
	for i := 0; i < b.N; i++ {
		ThanSumPastUint64(sample, 3, true)
	}
}

func BenchmarkThanSumPast(b *testing.B) {
	var sample = []float64{10.1, 11.1, 12.1, 53.1}
	for i := 0; i < b.N; i++ {
		thanSumPast(sample, true)
	}
}

func TestRanInt(t *testing.T) {
	if (RandInt() - RandInt() + RandInt() - RandInt()) == 0 {
		t.Error("Should not be the same.")
	}
}

func TestParseDate(t *testing.T) {
	var sample1 = "104/2/28"
	if ParseDate(sample1) != time.Date(2015, 2, 28, 0, 0, 0, 0, TaipeiTimeZone) {
		t.Error("Should be 2015/2/28")
	}
	var sample2 = "104/2/29"
	if ParseDate(sample2) != time.Date(2015, 3, 1, 0, 0, 0, 0, TaipeiTimeZone) {
		t.Error("Should be 2015/3/1")
	}
	var sample3 = "104/4/31"
	if ParseDate(sample3) != time.Date(2015, 5, 1, 0, 0, 0, 0, TaipeiTimeZone) {
		t.Error("Should be 2015/5/1")
	}
}

func TestAvg(t *testing.T) {
	var sample1 = []float64{3.3, 6.6, 9.9}
	if AvgFloat64(sample1) != 6.59 {
		t.Error("Should be 6.59")
	}
	var sample2 = []uint64{3, 6, 9}
	if AvgUint64(sample2) != 6 {
		t.Error("Should be 6")
	}
}

func TestSum(t *testing.T) {
	var sample1 = []float64{1.1, 2.2, 3.3}
	if SumFloat64(sample1) != 6.6 {
		t.Error("Should be 6.6")
	}
	var sample2 = []uint64{1, 2, 3}
	if SumUint64(sample2) != 6 {
		t.Error("Should be 6")
	}
}

func TestThanPast(t *testing.T) {
	var sample1 = []float64{10.1, 11.1, 12.1, 13.1}
	if !ThanPastFloat64(sample1, 3, true) {
		t.Error("Should be `true`")
	}
	var sample2 = []float64{10.1, 11.1, 12.1, 9.1}
	if !ThanPastFloat64(sample2, 3, false) {
		t.Error("Should be `true`")
	}
	if ThanPastFloat64(sample2, 3, true) {
		t.Error("Should be `false`")
	}
	var sample3 = []uint64{10, 11, 12, 13}
	if !ThanPastUint64(sample3, 3, true) {
		t.Error("Should be `true`")
	}
	var sample4 = []uint64{10, 11, 12, 9}
	if !ThanPastUint64(sample4, 3, false) {
		t.Error("Should be `true`")
	}
}

func TestThanSumPast(t *testing.T) {
	var sample1 = []float64{10.1, 11.1, 12.1, 53.1}
	if !ThanSumPastFloat64(sample1, 3, true) {
		t.Error("Should be `true`")
	}
	var sample2 = []float64{10.1, 11.1, 12.1, 10.1}
	if !ThanSumPastFloat64(sample2, 3, false) {
		t.Error("Should be `true`")
	}
	var sample3 = []uint64{10, 11, 12, 53}
	if !ThanSumPastUint64(sample3, 3, true) {
		t.Error("Should be `true`")
	}
	var sample4 = []uint64{10, 11, 12, 10}
	if !ThanSumPastUint64(sample4, 3, false) {
		t.Error("Should be `true`")
	}
}

func BenchmarkCountCountine(b *testing.B) {
	var sample1 = []float64{10.1, 11.1, 12.1, 53.1}
	for i := 0; i < b.N; i++ {
		CountCountineFloat64(sample1)
	}
}

func TestCountCountine(t *testing.T) {
	var sample1 = []float64{10.1, 11.1, 12.1, 53.1}  // 4 true
	var sample2 = []float64{10.1, 11.1, -12.1, 53.1} // 1 true
	var sample3 = []float64{10.1, 11.1, 12.1, -53.1} // 1 false
	if times, max := CountCountineFloat64(sample1); times != 4 && max != true {
		t.Error("Should be `4 true`")
	}
	if times, max := CountCountineFloat64(sample2); times != 1 && max != true {
		t.Error("Should be `1 true`")
	}
	if times, max := CountCountineFloat64(sample3); times != 1 && max != false {
		t.Error("Should be `1 false`")
	}
}

func TestCalDiff(t *testing.T) {
	var sampleA = []float64{10.0, 11.1, 12.2, 13.3}
	var sampleB = []float64{12.2, 11.1, 10.0}
	var result = CalDiffFloat(sampleA, sampleB)
	if result[2] != float64(13.3)-float64(10.0) {
		t.Error("Wrong cal.")
	}

	var sampleC = []int64{10, 11, 12, 13}
	var sampleD = []int64{12, 11, 10}
	var result2 = CalDiffInt64(sampleC, sampleD)
	if result2[2] != 13-10 {
		t.Error("Wrong cal.")
	}
}

func BenchmarkCalDiff(b *testing.B) {
	var sampleA = []float64{10.0, 11.1, 12.2, 13.3}
	var sampleB = []float64{12.2, 11.1, 10.0}
	for i := 0; i < b.N; i++ {
		CalDiffFloat(sampleA, sampleB)
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package loglist2 allows parsing and searching of the master CT Log list.
// It expects the log list to conform to the v2beta schema.
package loglist2

import (
	"bytes"
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"time"
	"unicode"

	"github.com/google/certificate-transparency-go/tls"
)

const (
	// LogListURL has the master URL for Google Chrome's log list.
	LogListURL = "https://www.gstatic.com/ct/log_list/v2/log_list.json"
	// LogListSignatureURL has the URL for the signature over Google Chrome's log list.
	LogListSignatureURL = "https://www.gstatic.com/ct/log_list/v2/log_list.sig"
	// AllLogListURL has the URL for the list of all known logs (which isn't signed).
	AllLogListURL = "https://www.gstatic.com/ct/log_list/v2/all_logs_list.json"
)

// Manually mapped from https://www.gstatic.com/ct/log_list/v2beta/log_list_schema.json

// LogList holds a collection of CT logs, grouped by operator.
type LogList struct {
	// Operators is a list of CT log operators and the logs they operate.
	Operators []*Operator `json:"operators"`
}

// Operator holds a collection of CT logs run by the same organisation.
// It also provides information about that organisation, e.g. contact details.
type Operator struct {
	// Name is the name of the CT log operator.
	Name string `json:"name"`
	// Email lists the email addresses that can be used to contact this log
	// operator.
	Email []string `json:"email"`
	// Logs is a list of CT logs run by this operator.
	Logs []*Log `json:"logs"`
}

// Log describes a single CT log.
type Log struct {
	// Description is a human-readable string that describes the log.
	Description string `json:"description,omitempty"`
	// LogID is the SHA-256 hash of the log's public key.
	LogID []byte `json:"log_id"`
	// Key is the public key with which signatures can be verified.
	Key []byte `json:"key"`
	// URL is the address of the HTTPS API.
	URL string `json:"url"`
	// DNS is the address of the DNS API.
	DNS string `json:"dns,omitempty"`
	// MMD is the Maximum Merge Delay, in seconds. All submitted
	// certificates must be incorporated into the log within this time.
	MMD int32 `json:"mmd"`
	// State is the current state of the log, from the perspective of the
	// log list distributor.
	State *LogStates `json:"state,omitempty"`
	// TemporalInterval, if set, indicates that this log only accepts
	// certificates with a NotBefore date in this time range.
	TemporalInterval *TemporalInterval `json:"temporal_interval,omitempty"`
	// Type indicates the purpose of this log, e.g. "test" or "prod".
	Type string `json:"log_type,omitempty"`
}

// TemporalInterval is a time range.
type TemporalInterval struct {
	// StartInclusive is the beginning of the time range.
	StartInclusive time.Time `json:"start_inclusive"`
	// EndExclusive is just after the end of the time range.
	EndExclusive time.Time `json:"end_exclusive"`
}

// LogStatus indicates Log status.
type LogStatus int

// LogStatus values
const (
	UndefinedLogStatus LogStatus = iota
	PendingLogStatus
	QualifiedLogStatus
	UsableLogStatus
	ReadOnlyLogStatus
	RetiredLogStatus
	RejectedLogStatus
)

//go:generate stringer -type=LogStatus

// LogStates are the states that a CT log can be in, from the perspective of a
// user agent. Only one should be set - this is the current state.
type LogStates struct {
	// Pending indicates that the log is in the "pending" state.
	Pending *LogState `json:"pending,omitempty"`
	// Qualified indicates that the log is in the "qualified" state.
	Qualified *LogState `json:"qualified,omitempty"`
	// Usable indicates that the log is in the "usable" state.
	Usable *LogState `json:"usable,omitempty"`
	// ReadOnly indicates that the log is in the "readonly" state.
	ReadOnly *ReadOnlyLogState `json:"readonly,omitempty"`
	// Retired indicates that the log is in the "retired" state.
	Retired *LogState `json:"retired,omitempty"`
	// Rejected indicates that the log is in the "rejected" state.
	Rejected *LogState `json:"rejected,omitempty"`
}

// LogState contains details on the current state of a CT log.
type LogState struct {
	// Timestamp is the time when the state began.
	Timestamp time.Time `json:"timestamp"`
}

// ReadOnlyLogState contains details on the current state of a read-only CT log.
type ReadOnlyLogState struct {
	LogState
	// FinalTreeHead is the root hash and tree size at which the CT log was
	// made read-only. This should never change while the log is read-only.
	FinalTreeHead TreeHead `json:"final_tree_head"`
}

// TreeHead is the root hash and tree size of a CT log.
type TreeHead struct {
	// SHA256RootHash is the root hash of the CT log's Merkle tree.
	SHA256RootHash []byte `json:"sha256_root_hash"`
	// TreeSize is the size of the CT log's Merkle tree.
	TreeSize int64 `json:"tree_size"`
}

// LogStatus method returns Log-status enum value for descriptive struct.
func (ls *LogStates) LogStatus() LogStatus {
	switch {
	case ls == nil:
		return UndefinedLogStatus
	case ls.Pending != nil:
		return PendingLogStatus
	case ls.Qualified != nil:
		return QualifiedLogStatus
	case ls.Usable != nil:
		return UsableLogStatus
	case ls.ReadOnly != nil:
		return ReadOnlyLogStatus
	case ls.Retired != nil:
		return RetiredLogStatus
	case ls.Rejected != nil:
		return RejectedLogStatus
	default:
		return UndefinedLogStatus
	}
}

// String method returns printable name of the state.
func (ls *LogStates) String() string {
	return ls.LogStatus().String()
}

// Active picks the set-up state. If multiple states are set (not expected) picks one of them.
func (ls *LogStates) Active() (*LogState, *ReadOnlyLogState) {
	if ls == nil {
		return nil, nil
	}
	switch {
	case ls.Pending != nil:
		return ls.Pending, nil
	case ls.Qualified != nil:
		return ls.Qualified, nil
	case ls.Usable != nil:
		return ls.Usable, nil
	case ls.ReadOnly != nil:
		return nil, ls.ReadOnly
	case ls.Retired != nil:
		return ls.Retired, nil
	case ls.Rejected != nil:
		return ls.Rejected, nil
	default:
		return nil, nil
	}
}

// GoogleOperated returns whether Operator is considered to be Google.
func (op *Operator) GoogleOperated() bool {
	for _, email := range op.Email {
		if strings.Contains(email, "google-ct-logs@googlegroups") {
			return true
		}
	}
	return false
}

// NewFromJSON creates a LogList from JSON encoded data.
func NewFromJSON(llData []byte) (*LogList, error) {
	var ll LogList
	if err := json.Unmarshal(llData, &ll); err != nil {
		return nil, fmt.Errorf("failed to parse log list: %v", err)
	}
	return &ll, nil
}

// NewFromSignedJSON creates a LogList from JSON encoded data, checking a
// signature along the way. The signature data should be provided as the
// raw signature data.
func NewFromSignedJSON(llData, rawSig []byte, pubKey crypto.PublicKey) (*LogList, error) {
	var sigAlgo tls.SignatureAlgorithm
	switch pkType := pubKey.(type) {
	case *rsa.PublicKey:
		sigAlgo = tls.RSA
	case *ecdsa.PublicKey:
		sigAlgo = tls.ECDSA
	default:
		return nil, fmt.Errorf("unsupported public key type %v", pkType)
	}
	tlsSig := tls.DigitallySigned{
		Algorithm: tls.SignatureAndHashAlgorithm{
			Hash:      tls.SHA256,
			Signature: sigAlgo,
		},
		Signature: rawSig,
	}
	if err := tls.VerifySignature(pubKey, llData, tlsSig); err != nil {
		return nil, fmt.Errorf("failed to verify signature: %v", err)
	}
	return NewFromJSON(llData)
}

// FindLogByName returns all logs whose names contain the given string.
func (ll *LogList) FindLogByName(name string) []*Log {
	name = strings.ToLower(name)
	var results []*Log
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			if strings.Contains(strings.ToLower(log.Description), name) {
				results = append(results, log)
			}
		}
	}
	return results
}

// FindLogByURL finds the log with the given URL.
func (ll *LogList) FindLogByURL(url string) *Log {
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			// Don't count trailing slashes
			if strings.TrimRight(log.URL, "/") == strings.TrimRight(url, "/") {
				return log
			}
		}
	}
	return nil
}

// FindLogByKeyHash finds the log with the given key hash.
func (ll *LogList) FindLogByKeyHash(keyhash [sha256.Size]byte) *Log {
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			if bytes.Equal(log.LogID, keyhash[:]) {
				return log
			}
		}
	}
	return nil
}

// FindLogByKeyHashPrefix finds all logs whose key hash starts with the prefix.
func (ll *LogList) FindLogByKeyHashPrefix(prefix string) []*Log {
	var results []*Log
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			hh := hex.EncodeToString(log.LogID[:])
			if strings.HasPrefix(hh, prefix) {
				results = append(results, log)
			}
		}
	}
	return results
}

// FindLogByKey finds the log with the given DER-encoded key.
func (ll *LogList) FindLogByKey(key []byte) *Log {
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			if bytes.Equal(log.Key[:], key) {
				return log
			}
		}
	}
	return nil
}

var hexDigits = regexp.MustCompile("^[0-9a-fA-F]+$")

// FuzzyFindLog tries to find logs that match the given unspecified input,
// whose format is unspecified.  This generally returns a single log, but
// if text input that matches multiple log descriptions is provided, then
// multiple logs may be returned.
func (ll *LogList) FuzzyFindLog(input string) []*Log {
	input = strings.Trim(input, " \t")
	if logs := ll.FindLogByName(input); len(logs) > 0 {
		return logs
	}
	if log := ll.FindLogByURL(input); log != nil {
		return []*Log{log}
	}
	// Try assuming the input is binary data of some form.  First base64:
	if data, err := base64.StdEncoding.DecodeString(input); err == nil {
		if len(data) == sha256.Size {
			var hash [sha256.Size]byte
			copy(hash[:], data)
			if log := ll.FindLogByKeyHash(hash); log != nil {
				return []*Log{log}
			}
		}
		if log := ll.FindLogByKey(data); log != nil {
			return []*Log{log}
		}
	}
	// Now hex, but strip all internal whitespace first.
	input = stripInternalSpace(input)
	if data, err := hex.DecodeString(input); err == nil {
		if len(data) == sha256.Size {
			var hash [sha256.Size]byte
			copy(hash[:], data)
			if log := ll.FindLogByKeyHash(hash); log != nil {
				return []*Log{log}
			}
		}
		if log := ll.FindLogByKey(data); log != nil {
			return []*Log{log}
		}
	}
	// Finally, allow hex strings with an odd number of digits.
	if hexDigits.MatchString(input) {
		if logs := ll.FindLogByKeyHashPrefix(input); len(logs) > 0 {
			return logs
		}
	}

	return nil
}

func stripInternalSpace(input string) string {
	return strings.Map(func(r rune) rune {
		if !unicode.IsSpace(r) {
			return r
		}
		return -1
	}, input)
}
<commit_msg>NotAfter is used for temporal sharding of logs<commit_after>// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package loglist2 allows parsing and searching of the master CT Log list.
// It expects the log list to conform to the v2beta schema.
package loglist2

import (
	"bytes"
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"time"
	"unicode"

	"github.com/google/certificate-transparency-go/tls"
)

const (
	// LogListURL has the master URL for Google Chrome's log list.
	LogListURL = "https://www.gstatic.com/ct/log_list/v2/log_list.json"
	// LogListSignatureURL has the URL for the signature over Google Chrome's log list.
	LogListSignatureURL = "https://www.gstatic.com/ct/log_list/v2/log_list.sig"
	// AllLogListURL has the URL for the list of all known logs (which isn't signed).
	AllLogListURL = "https://www.gstatic.com/ct/log_list/v2/all_logs_list.json"
)

// Manually mapped from https://www.gstatic.com/ct/log_list/v2beta/log_list_schema.json

// LogList holds a collection of CT logs, grouped by operator.
type LogList struct {
	// Operators is a list of CT log operators and the logs they operate.
	Operators []*Operator `json:"operators"`
}

// Operator holds a collection of CT logs run by the same organisation.
// It also provides information about that organisation, e.g. contact details.
type Operator struct {
	// Name is the name of the CT log operator.
	Name string `json:"name"`
	// Email lists the email addresses that can be used to contact this log
	// operator.
	Email []string `json:"email"`
	// Logs is a list of CT logs run by this operator.
	Logs []*Log `json:"logs"`
}

// Log describes a single CT log.
type Log struct {
	// Description is a human-readable string that describes the log.
	Description string `json:"description,omitempty"`
	// LogID is the SHA-256 hash of the log's public key.
	LogID []byte `json:"log_id"`
	// Key is the public key with which signatures can be verified.
	Key []byte `json:"key"`
	// URL is the address of the HTTPS API.
	URL string `json:"url"`
	// DNS is the address of the DNS API.
	DNS string `json:"dns,omitempty"`
	// MMD is the Maximum Merge Delay, in seconds. All submitted
	// certificates must be incorporated into the log within this time.
	MMD int32 `json:"mmd"`
	// State is the current state of the log, from the perspective of the
	// log list distributor.
	State *LogStates `json:"state,omitempty"`
	// TemporalInterval, if set, indicates that this log only accepts
	// certificates with a NotAfter date in this time range.
	TemporalInterval *TemporalInterval `json:"temporal_interval,omitempty"`
	// Type indicates the purpose of this log, e.g. "test" or "prod".
	Type string `json:"log_type,omitempty"`
}

// TemporalInterval is a time range.
type TemporalInterval struct {
	// StartInclusive is the beginning of the time range.
	StartInclusive time.Time `json:"start_inclusive"`
	// EndExclusive is just after the end of the time range.
	EndExclusive time.Time `json:"end_exclusive"`
}

// LogStatus indicates Log status.
type LogStatus int

// LogStatus values
const (
	UndefinedLogStatus LogStatus = iota
	PendingLogStatus
	QualifiedLogStatus
	UsableLogStatus
	ReadOnlyLogStatus
	RetiredLogStatus
	RejectedLogStatus
)

//go:generate stringer -type=LogStatus

// LogStates are the states that a CT log can be in, from the perspective of a
// user agent. Only one should be set - this is the current state.
type LogStates struct {
	// Pending indicates that the log is in the "pending" state.
	Pending *LogState `json:"pending,omitempty"`
	// Qualified indicates that the log is in the "qualified" state.
	Qualified *LogState `json:"qualified,omitempty"`
	// Usable indicates that the log is in the "usable" state.
	Usable *LogState `json:"usable,omitempty"`
	// ReadOnly indicates that the log is in the "readonly" state.
	ReadOnly *ReadOnlyLogState `json:"readonly,omitempty"`
	// Retired indicates that the log is in the "retired" state.
	Retired *LogState `json:"retired,omitempty"`
	// Rejected indicates that the log is in the "rejected" state.
	Rejected *LogState `json:"rejected,omitempty"`
}

// LogState contains details on the current state of a CT log.
type LogState struct {
	// Timestamp is the time when the state began.
	Timestamp time.Time `json:"timestamp"`
}

// ReadOnlyLogState contains details on the current state of a read-only CT log.
type ReadOnlyLogState struct {
	LogState
	// FinalTreeHead is the root hash and tree size at which the CT log was
	// made read-only. This should never change while the log is read-only.
	FinalTreeHead TreeHead `json:"final_tree_head"`
}

// TreeHead is the root hash and tree size of a CT log.
type TreeHead struct {
	// SHA256RootHash is the root hash of the CT log's Merkle tree.
	SHA256RootHash []byte `json:"sha256_root_hash"`
	// TreeSize is the size of the CT log's Merkle tree.
	TreeSize int64 `json:"tree_size"`
}

// LogStatus method returns Log-status enum value for descriptive struct.
func (ls *LogStates) LogStatus() LogStatus {
	switch {
	case ls == nil:
		return UndefinedLogStatus
	case ls.Pending != nil:
		return PendingLogStatus
	case ls.Qualified != nil:
		return QualifiedLogStatus
	case ls.Usable != nil:
		return UsableLogStatus
	case ls.ReadOnly != nil:
		return ReadOnlyLogStatus
	case ls.Retired != nil:
		return RetiredLogStatus
	case ls.Rejected != nil:
		return RejectedLogStatus
	default:
		return UndefinedLogStatus
	}
}

// String method returns printable name of the state.
func (ls *LogStates) String() string {
	return ls.LogStatus().String()
}

// Active picks the set-up state. If multiple states are set (not expected) picks one of them.
func (ls *LogStates) Active() (*LogState, *ReadOnlyLogState) {
	if ls == nil {
		return nil, nil
	}
	switch {
	case ls.Pending != nil:
		return ls.Pending, nil
	case ls.Qualified != nil:
		return ls.Qualified, nil
	case ls.Usable != nil:
		return ls.Usable, nil
	case ls.ReadOnly != nil:
		return nil, ls.ReadOnly
	case ls.Retired != nil:
		return ls.Retired, nil
	case ls.Rejected != nil:
		return ls.Rejected, nil
	default:
		return nil, nil
	}
}

// GoogleOperated returns whether Operator is considered to be Google.
func (op *Operator) GoogleOperated() bool {
	for _, email := range op.Email {
		if strings.Contains(email, "google-ct-logs@googlegroups") {
			return true
		}
	}
	return false
}

// NewFromJSON creates a LogList from JSON encoded data.
func NewFromJSON(llData []byte) (*LogList, error) {
	var ll LogList
	if err := json.Unmarshal(llData, &ll); err != nil {
		return nil, fmt.Errorf("failed to parse log list: %v", err)
	}
	return &ll, nil
}

// NewFromSignedJSON creates a LogList from JSON encoded data, checking a
// signature along the way. The signature data should be provided as the
// raw signature data.
func NewFromSignedJSON(llData, rawSig []byte, pubKey crypto.PublicKey) (*LogList, error) {
	var sigAlgo tls.SignatureAlgorithm
	switch pkType := pubKey.(type) {
	case *rsa.PublicKey:
		sigAlgo = tls.RSA
	case *ecdsa.PublicKey:
		sigAlgo = tls.ECDSA
	default:
		return nil, fmt.Errorf("unsupported public key type %v", pkType)
	}
	tlsSig := tls.DigitallySigned{
		Algorithm: tls.SignatureAndHashAlgorithm{
			Hash:      tls.SHA256,
			Signature: sigAlgo,
		},
		Signature: rawSig,
	}
	if err := tls.VerifySignature(pubKey, llData, tlsSig); err != nil {
		return nil, fmt.Errorf("failed to verify signature: %v", err)
	}
	return NewFromJSON(llData)
}

// FindLogByName returns all logs whose names contain the given string.
func (ll *LogList) FindLogByName(name string) []*Log {
	name = strings.ToLower(name)
	var results []*Log
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			if strings.Contains(strings.ToLower(log.Description), name) {
				results = append(results, log)
			}
		}
	}
	return results
}

// FindLogByURL finds the log with the given URL.
func (ll *LogList) FindLogByURL(url string) *Log {
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			// Don't count trailing slashes
			if strings.TrimRight(log.URL, "/") == strings.TrimRight(url, "/") {
				return log
			}
		}
	}
	return nil
}

// FindLogByKeyHash finds the log with the given key hash.
func (ll *LogList) FindLogByKeyHash(keyhash [sha256.Size]byte) *Log {
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			if bytes.Equal(log.LogID, keyhash[:]) {
				return log
			}
		}
	}
	return nil
}

// FindLogByKeyHashPrefix finds all logs whose key hash starts with the prefix.
func (ll *LogList) FindLogByKeyHashPrefix(prefix string) []*Log {
	var results []*Log
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			hh := hex.EncodeToString(log.LogID[:])
			if strings.HasPrefix(hh, prefix) {
				results = append(results, log)
			}
		}
	}
	return results
}

// FindLogByKey finds the log with the given DER-encoded key.
func (ll *LogList) FindLogByKey(key []byte) *Log {
	for _, op := range ll.Operators {
		for _, log := range op.Logs {
			if bytes.Equal(log.Key[:], key) {
				return log
			}
		}
	}
	return nil
}

var hexDigits = regexp.MustCompile("^[0-9a-fA-F]+$")

// FuzzyFindLog tries to find logs that match the given unspecified input,
// whose format is unspecified.  This generally returns a single log, but
// if text input that matches multiple log descriptions is provided, then
// multiple logs may be returned.
func (ll *LogList) FuzzyFindLog(input string) []*Log {
	input = strings.Trim(input, " \t")
	if logs := ll.FindLogByName(input); len(logs) > 0 {
		return logs
	}
	if log := ll.FindLogByURL(input); log != nil {
		return []*Log{log}
	}
	// Try assuming the input is binary data of some form.  First base64:
	if data, err := base64.StdEncoding.DecodeString(input); err == nil {
		if len(data) == sha256.Size {
			var hash [sha256.Size]byte
			copy(hash[:], data)
			if log := ll.FindLogByKeyHash(hash); log != nil {
				return []*Log{log}
			}
		}
		if log := ll.FindLogByKey(data); log != nil {
			return []*Log{log}
		}
	}
	// Now hex, but strip all internal whitespace first.
	input = stripInternalSpace(input)
	if data, err := hex.DecodeString(input); err == nil {
		if len(data) == sha256.Size {
			var hash [sha256.Size]byte
			copy(hash[:], data)
			if log := ll.FindLogByKeyHash(hash); log != nil {
				return []*Log{log}
			}
		}
		if log := ll.FindLogByKey(data); log != nil {
			return []*Log{log}
		}
	}
	// Finally, allow hex strings with an odd number of digits.
	if hexDigits.MatchString(input) {
		if logs := ll.FindLogByKeyHashPrefix(input); len(logs) > 0 {
			return logs
		}
	}

	return nil
}

func stripInternalSpace(input string) string {
	return strings.Map(func(r rune) rune {
		if !unicode.IsSpace(r) {
			return r
		}
		return -1
	}, input)
}
<|endoftext|>


<commit_before>package flat

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"

	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

const (
	minMTU = 68
)

/*
type FlatConfig struct {
	IP     string
	GW     string
	CName  string
	HostIF string
	MTU    int
}
*/

// ctx.Args()[0] args are not the parameters, fmz。
func Flat(ctx *cli.Context) {
	err := VerifyFlatParam(ctx)
	if err != nil {
		fmt.Print(err)
	} else {

	}
}

// log.fatalf will block the process and return errors. so the errors.new can be remove,fmz.
// when no param pass, it will not equal "", because i set the value(default) first, fmz.
func VerifyFlatParam(ctx *cli.Context) error {
	if ctx.String("host-interface") == "" {
		log.Fatalf("Required flag [ host-interface ] is missing")
		return errors.New("Required flag [ host-interface ] is missing")
	}

	cliIF = ctx.String("host-interface")
	if ctx.String("ip") == "" || ctx.String("gateway") == "" || ctx.String("container-name") == "" {
		log.Fatalf("Required flag [ ip or gateway ] is missing")
		return errors.New("Required flag [ ip or gateway or container-name ] is missing")
	}

	cliIP = ctx.String("ip")
	cligwIP = ctx.String("gateway")
	cliCName = ctx.String("container-name")

	if ctx.Int("mtu") <= 0 {
		cliMTU = cliMTU
	} else if ctx.Int("mtu") >= minMTU {
		cliMTU = ctx.Int("mtu")
	} else {
		log.Fatalf("The MTU value passed [ %d ] must be greater than [ %d ] bytes per rfc791", ctx.Int("mtu"), minMTU)
		return errors.New("the mtu must be int")
	}
	return nil
}

//netlink is not avaible in MAC. build fail
func AddContainerNetworking() {
	//create the macvlan device
	macvlandev := &netlink.Macvlan{
		LinkAttrs: netlink.LinkAttrs{
			Name:        preMoveName,
			ParentIndex: hostEth.Attrs().Index,
		},
		Mode: mode,
	}
	netns.Set()
}
<commit_msg>9.5<commit_after>package flat

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"

	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

const (
	minMTU = 68
)

/*
type FlatConfig struct {
	IP     string
	GW     string
	CName  string
	HostIF string
	MTU    int
}
*/

// ctx.Args()[0] args are not the parameters, fmz。
func Flat(ctx *cli.Context) {
	err := VerifyFlatParam(ctx)
	if err != nil {
		fmt.Print(err)
	} else {

	}
}

// log.fatalf will block the process and return errors. so the errors.new can be remove,fmz.
// when no param pass, it will not equal "", because i set the value(default) first, fmz.
func VerifyFlatParam(ctx *cli.Context) error {
	if ctx.String("host-interface") == "" {
		log.Fatalf("Required flag [ host-interface ] is missing")
		return errors.New("Required flag [ host-interface ] is missing")
	}

	cliIF = ctx.String("host-interface")
	if ctx.String("ip") == "" || ctx.String("gateway") == "" || ctx.String("container-name") == "" {
		log.Fatalf("Required flag [ ip or gateway ] is missing")
		return errors.New("Required flag [ ip or gateway or container-name ] is missing")
	}

	cliIP = ctx.String("ip")
	cligwIP = ctx.String("gateway")
	cliCName = ctx.String("container-name")

	if ctx.Int("mtu") <= 0 {
		cliMTU = cliMTU
	} else if ctx.Int("mtu") >= minMTU {
		cliMTU = ctx.Int("mtu")
	} else {
		log.Fatalf("The MTU value passed [ %d ] must be greater than [ %d ] bytes per rfc791", ctx.Int("mtu"), minMTU)
		return errors.New("the mtu must be int")
	}
	return nil
}

//netlink is not avaible in MAC. build fail
func AddContainerNetworking() {
	//create the macvlan device
	macvlandev := &netlink.Macvlan{
		LinkAttrs: netlink.LinkAttrs{
			Name:        preMoveName,
			ParentIndex: hostEth.Attrs().Index,
		},
		Mode: mode,
	}

}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"image/color"
	"runtime"
	"strings"
	"sync"
	"time"

	"gopkg.in/fsnotify.v0"
	"gopkg.in/qml.v1"

	"github.com/limetext/lime-backend/lib"
	"github.com/limetext/lime-backend/lib/keys"
	"github.com/limetext/lime-backend/lib/log"
	"github.com/limetext/lime-backend/lib/render"
	_ "github.com/limetext/lime-backend/lib/sublime"
	"github.com/limetext/lime-backend/lib/textmate"
	"github.com/limetext/lime-backend/lib/util"
	. "github.com/limetext/text"
)

var (
	limeViewComponent qml.Object
	scheme            *textmate.Theme
)

const (
	batching_enabled = true
	qmlMainFile      = "main.qml"
	qmlViewFile      = "LimeView.qml"

	// http://qt-project.org/doc/qt-5.1/qtcore/qt.html#KeyboardModifier-enum
	shift_mod  = 0x02000000
	ctrl_mod   = 0x04000000
	alt_mod    = 0x08000000
	meta_mod   = 0x10000000
	keypad_mod = 0x20000000
)

// keeping track of frontend state
type qmlfrontend struct {
	status_message string
	lock           sync.Mutex
	windows        map[*backend.Window]*frontendWindow
	Console        *frontendView
	qmlDispatch    chan qmlDispatch
}

// Used for batching qml.Changed calls
type qmlDispatch struct{ value, field interface{} }

func (t *qmlfrontend) Window(w *backend.Window) *frontendWindow {
	return t.windows[w]
}

func (t *qmlfrontend) Show(v *backend.View, r Region) {
	// TODO
}

func (t *qmlfrontend) VisibleRegion(v *backend.View) Region {
	// TODO
	return Region{0, v.Buffer().Size()}
}

func (t *qmlfrontend) StatusMessage(msg string) {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.status_message = msg
}

func (t *qmlfrontend) ErrorMessage(msg string) {
	log.Error(msg)
	var q qmlDialog
	q.Show(msg, "StandardIcon.Critical")
}

func (t *qmlfrontend) MessageDialog(msg string) {
	var q qmlDialog
	q.Show(msg, "StandardIcon.Information")
}

func (t *qmlfrontend) OkCancelDialog(msg, ok string) bool {
	var q qmlDialog
	return q.Show(msg, "StandardIcon.Question") == 1
}

func (t *qmlfrontend) scroll(b Buffer) {
	t.Show(backend.GetEditor().Console(), Region{b.Size(), b.Size()})
}

func (t *qmlfrontend) Erased(changed_buffer Buffer, region_removed Region, data_removed []rune) {
	t.scroll(changed_buffer)
}

func (t *qmlfrontend) Inserted(changed_buffer Buffer, region_inserted Region, data_inserted []rune) {
	t.scroll(changed_buffer)
}

// Apparently calling qml.Changed also triggers a re-draw, meaning that typed text is at the
// mercy of how quick Qt happens to be rendering.
// Try setting batching_enabled = false to see the effects of non-batching
func (t *qmlfrontend) qmlBatchLoop() {
	queue := make(map[qmlDispatch]bool)
	t.qmlDispatch = make(chan qmlDispatch, 1000)
	for {
		if len(queue) > 0 {
			select {
			case <-time.After(time.Millisecond * 20):
				// Nothing happened for 20 milliseconds, so dispatch all queued changes
				for k := range queue {
					qml.Changed(k.value, k.field)
				}
				queue = make(map[qmlDispatch]bool)
			case d := <-t.qmlDispatch:
				queue[d] = true
			}
		} else {
			queue[<-t.qmlDispatch] = true
		}
	}
}

func (t *qmlfrontend) qmlChanged(value, field interface{}) {
	if !batching_enabled {
		qml.Changed(value, field)
	} else {
		t.qmlDispatch <- qmlDispatch{value, field}
	}
}

func (t *qmlfrontend) DefaultBg() color.RGBA {
	c := scheme.Spice(&render.ViewRegions{})
	c.Background.A = 0xff
	return color.RGBA(c.Background)
}

func (t *qmlfrontend) DefaultFg() color.RGBA {
	c := scheme.Spice(&render.ViewRegions{})
	c.Foreground.A = 0xff
	return color.RGBA(c.Foreground)
}

// Called when a new view is opened
func (t *qmlfrontend) onNew(v *backend.View) {
	fv := &frontendView{bv: v}
	v.Buffer().AddObserver(fv)
	v.Settings().AddOnChange("blah", fv.onChange)

	fv.Title.Text = v.Buffer().FileName()
	if len(fv.Title.Text) == 0 {
		fv.Title.Text = "untitled"
	}

	w2 := t.windows[v.Window()]
	w2.views = append(w2.views, fv)

	tabs := w2.window.ObjectByName("tabs")
	tab := tabs.Call("addTab", "", limeViewComponent).(qml.Object)
	try_now := func() {
		item := tab.Property("item").(qml.Object)
		if item.Addr() == 0 {
			// Happens as the item isn't actually loaded until we switch to the tab.
			// Hence connecting to the loaded signal
			return
		}
		item.Set("myView", fv)
		item.Set("fontSize", float64(v.Settings().Get("font_size", 12).(int)))
		item.Set("fontFace", v.Settings().Get("font_face", "Helvetica").(string))
	}
	tab.On("loaded", try_now)
	try_now()
	tabs.Set("currentIndex", tabs.Property("count").(int)-1)
}

// called when a view is closed
func (t *qmlfrontend) onClose(v *backend.View) {
	w2 := t.windows[v.Window()]
	for i := range w2.views {
		if w2.views[i].bv == v {
			w2.window.ObjectByName("tabs").Call("removeTab", i)
			copy(w2.views[i:], w2.views[i+1:])
			w2.views = w2.views[:len(w2.views)-1]
			return
		}
	}
	log.Error("Couldn't find closed view...")
}

// called when a view has loaded
func (t *qmlfrontend) onLoad(v *backend.View) {
	w2 := t.windows[v.Window()]
	i := 0
	for i = range w2.views {
		if w2.views[i].bv == v {
			break
		}
	}
	v2 := w2.views[i]
	v2.Title.Text = v.Buffer().FileName()
	tabs := w2.window.ObjectByName("tabs")
	tabs.Set("currentIndex", w2.ActiveViewIndex())
	tab := tabs.Call("getTab", i).(qml.Object)
	tab.Set("title", v2.Title.Text)
}

func (t *qmlfrontend) onSelectionModified(v *backend.View) {
	w2 := t.windows[v.Window()]
	i := 0
	for i = range w2.views {
		if w2.views[i].bv == v {
			break
		}
	}
	v2 := w2.views[i]
	v2.qv.Call("onSelectionModified")
}

// Launches the provided command in a new goroutine
// (to avoid locking up the GUI)
func (t *qmlfrontend) RunCommand(command string) {
	t.RunCommandWithArgs(command, make(backend.Args))
}

func (t *qmlfrontend) RunCommandWithArgs(command string, args backend.Args) {
	ed := backend.GetEditor()
	go ed.RunCommand(command, args)
}

func (t *qmlfrontend) HandleInput(text string, keycode int, modifiers int) bool {
	log.Debug("qmlfrontend.HandleInput: text=%v, key=%x, modifiers=%x", text, keycode, modifiers)
	shift := false
	alt := false
	ctrl := false
	super := false

	if key, ok := lut[keycode]; ok {
		ed := backend.GetEditor()

		if (modifiers & shift_mod) != 0 {
			shift = true
		}
		if (modifiers & alt_mod) != 0 {
			alt = true
		}
		if (modifiers & ctrl_mod) != 0 {
			if runtime.GOOS == "darwin" {
				super = true
			} else {
				ctrl = true
			}
		}
		if (modifiers & meta_mod) != 0 {
			if runtime.GOOS == "darwin" {
				ctrl = true
			} else {
				super = true
			}
		}

		ed.HandleInput(keys.KeyPress{Text: text, Key: key, Shift: shift, Alt: alt, Ctrl: ctrl, Super: super})
		return true
	}
	return false
}

// Quit closes all open windows to de-reference all qml objects
func (t *qmlfrontend) Quit() (err error) {
	// todo: handle changed files that aren't saved.
	for _, v := range t.windows {
		if v.window != nil {
			v.window.Hide()
			v.window.Destroy()
			v.window = nil
		}
	}
	return
}

func (t *qmlfrontend) loop() (err error) {
	backend.OnNew.Add(t.onNew)
	backend.OnClose.Add(t.onClose)
	backend.OnLoad.Add(t.onLoad)
	backend.OnSelectionModified.Add(t.onSelectionModified)

	ed := backend.GetEditor()
	ed.SetFrontend(t)
	ed.LogInput(false)
	ed.LogCommands(false)
	c := ed.Console()
	t.Console = &frontendView{bv: c}
	c.Buffer().AddObserver(t.Console)
	c.Buffer().AddObserver(t)
	go ed.Init()

	var (
		engine    *qml.Engine
		component qml.Object
		// WaitGroup keeping track of open windows
		wg sync.WaitGroup
	)

	// create and setup a new engine, destroying
	// the old one if one exists.
	//
	// This is needed to re-load qml files to get
	// the new file contents from disc as otherwise
	// the old file would still be what is referenced.
	newEngine := func() (err error) {
		if engine != nil {
			log.Debug("calling destroy")
			// TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now
			// engine.Destroy()
			engine = nil
		}
		log.Debug("calling newEngine")
		engine = qml.NewEngine()
		engine.On("quit", t.Quit)
		log.Debug("setvar frontend")
		engine.Context().SetVar("frontend", t)
		log.Debug("setvar editor")
		engine.Context().SetVar("editor", backend.GetEditor())

		log.Debug("loadfile")
		component, err = engine.LoadFile(qmlMainFile)
		if err != nil {
			return err
		}
		limeViewComponent, err = engine.LoadFile(qmlViewFile)
		return
	}
	if err := newEngine(); err != nil {
		log.Error(err)
	}

	backend.OnNewWindow.Add(func(w *backend.Window) {
		fw := &frontendWindow{bw: w}
		t.windows[w] = fw
		if component != nil {
			fw.launch(&wg, component)
		}
	})

	// TODO: should be done backend side
	if sc, err := textmate.LoadTheme("../packages/themes/TextMate-Themes/Monokai.tmTheme"); err != nil {
		log.Error(err)
	} else {
		scheme = sc
	}

	defer func() {
		fmt.Println(util.Prof)
	}()

	w := ed.NewWindow()
	v := w.OpenFile("main.go", 0)
	// TODO: should be done backend side
	v.SetSyntaxFile("../packages/go.tmbundle/Syntaxes/Go.tmLanguage")

	watch, err := fsnotify.NewWatcher()
	if err != nil {
		log.Error("Unable to create file watcher: %s", err)
		return
	}
	defer watch.Close()
	watch.Watch(".")
	defer watch.RemoveWatch(".")

	reloadRequested := false

	go func() {
		for {
			select {
			case ev := <-watch.Event:
				if ev != nil && strings.HasSuffix(ev.Name, ".qml") && ev.IsModify() && !ev.IsAttrib() {
					reloadRequested = true
					t.Quit()
				}
			}
		}
	}()

	for {
		// Reset reload status
		reloadRequested = false

		log.Debug("Waiting for all windows to close")
		// wg would be the WaitGroup all windows belong to, so first we wait for
		// all windows to close.
		wg.Wait()
		log.Debug("All windows closed. reloadRequest: %v", reloadRequested)
		// then we check if there's a reload request in the pipe
		if !reloadRequested || len(t.windows) == 0 {
			// This would be a genuine exit; all windows closed by the user
			break
		}

		// *We* closed all windows because we want to reload freshly changed qml
		// files.
		for {
			log.Debug("Calling newEngine")
			if err := newEngine(); err != nil {
				// Reset reload status
				reloadRequested = false
				log.Error(err)
				for !reloadRequested {
					// This loop allows us to re-try reloading
					// if there was an error in the file this time,
					// we just loop around again when we receive the next
					// reload request (ie on the next save of the file).
					time.Sleep(time.Second)
				}
				continue
			}
			log.Debug("break")
			break
		}
		log.Debug("re-launching all windows")
		// Succeeded loading the file, re-launch all windows
		for _, v := range t.windows {
			v.launch(&wg, component)
		}
	}
	return
}
<commit_msg>moved ed.Init() after adding OnNewWindow event<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"image/color"
	"runtime"
	"strings"
	"sync"
	"time"

	"gopkg.in/fsnotify.v0"
	"gopkg.in/qml.v1"

	"github.com/limetext/lime-backend/lib"
	"github.com/limetext/lime-backend/lib/keys"
	"github.com/limetext/lime-backend/lib/log"
	"github.com/limetext/lime-backend/lib/render"
	_ "github.com/limetext/lime-backend/lib/sublime"
	"github.com/limetext/lime-backend/lib/textmate"
	"github.com/limetext/lime-backend/lib/util"
	. "github.com/limetext/text"
)

var (
	limeViewComponent qml.Object
	scheme            *textmate.Theme
)

const (
	batching_enabled = true
	qmlMainFile      = "main.qml"
	qmlViewFile      = "LimeView.qml"

	// http://qt-project.org/doc/qt-5.1/qtcore/qt.html#KeyboardModifier-enum
	shift_mod  = 0x02000000
	ctrl_mod   = 0x04000000
	alt_mod    = 0x08000000
	meta_mod   = 0x10000000
	keypad_mod = 0x20000000
)

// keeping track of frontend state
type qmlfrontend struct {
	status_message string
	lock           sync.Mutex
	windows        map[*backend.Window]*frontendWindow
	Console        *frontendView
	qmlDispatch    chan qmlDispatch
}

// Used for batching qml.Changed calls
type qmlDispatch struct{ value, field interface{} }

func (t *qmlfrontend) Window(w *backend.Window) *frontendWindow {
	return t.windows[w]
}

func (t *qmlfrontend) Show(v *backend.View, r Region) {
	// TODO
}

func (t *qmlfrontend) VisibleRegion(v *backend.View) Region {
	// TODO
	return Region{0, v.Buffer().Size()}
}

func (t *qmlfrontend) StatusMessage(msg string) {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.status_message = msg
}

func (t *qmlfrontend) ErrorMessage(msg string) {
	log.Error(msg)
	var q qmlDialog
	q.Show(msg, "StandardIcon.Critical")
}

func (t *qmlfrontend) MessageDialog(msg string) {
	var q qmlDialog
	q.Show(msg, "StandardIcon.Information")
}

func (t *qmlfrontend) OkCancelDialog(msg, ok string) bool {
	var q qmlDialog
	return q.Show(msg, "StandardIcon.Question") == 1
}

func (t *qmlfrontend) scroll(b Buffer) {
	t.Show(backend.GetEditor().Console(), Region{b.Size(), b.Size()})
}

func (t *qmlfrontend) Erased(changed_buffer Buffer, region_removed Region, data_removed []rune) {
	t.scroll(changed_buffer)
}

func (t *qmlfrontend) Inserted(changed_buffer Buffer, region_inserted Region, data_inserted []rune) {
	t.scroll(changed_buffer)
}

// Apparently calling qml.Changed also triggers a re-draw, meaning that typed text is at the
// mercy of how quick Qt happens to be rendering.
// Try setting batching_enabled = false to see the effects of non-batching
func (t *qmlfrontend) qmlBatchLoop() {
	queue := make(map[qmlDispatch]bool)
	t.qmlDispatch = make(chan qmlDispatch, 1000)
	for {
		if len(queue) > 0 {
			select {
			case <-time.After(time.Millisecond * 20):
				// Nothing happened for 20 milliseconds, so dispatch all queued changes
				for k := range queue {
					qml.Changed(k.value, k.field)
				}
				queue = make(map[qmlDispatch]bool)
			case d := <-t.qmlDispatch:
				queue[d] = true
			}
		} else {
			queue[<-t.qmlDispatch] = true
		}
	}
}

func (t *qmlfrontend) qmlChanged(value, field interface{}) {
	if !batching_enabled {
		qml.Changed(value, field)
	} else {
		t.qmlDispatch <- qmlDispatch{value, field}
	}
}

func (t *qmlfrontend) DefaultBg() color.RGBA {
	c := scheme.Spice(&render.ViewRegions{})
	c.Background.A = 0xff
	return color.RGBA(c.Background)
}

func (t *qmlfrontend) DefaultFg() color.RGBA {
	c := scheme.Spice(&render.ViewRegions{})
	c.Foreground.A = 0xff
	return color.RGBA(c.Foreground)
}

// Called when a new view is opened
func (t *qmlfrontend) onNew(v *backend.View) {
	fv := &frontendView{bv: v}
	v.Buffer().AddObserver(fv)
	v.Settings().AddOnChange("blah", fv.onChange)

	fv.Title.Text = v.Buffer().FileName()
	if len(fv.Title.Text) == 0 {
		fv.Title.Text = "untitled"
	}

	w2 := t.windows[v.Window()]
	w2.views = append(w2.views, fv)

	tabs := w2.window.ObjectByName("tabs")
	tab := tabs.Call("addTab", "", limeViewComponent).(qml.Object)
	try_now := func() {
		item := tab.Property("item").(qml.Object)
		if item.Addr() == 0 {
			// Happens as the item isn't actually loaded until we switch to the tab.
			// Hence connecting to the loaded signal
			return
		}
		item.Set("myView", fv)
		item.Set("fontSize", float64(v.Settings().Get("font_size", 12).(int)))
		item.Set("fontFace", v.Settings().Get("font_face", "Helvetica").(string))
	}
	tab.On("loaded", try_now)
	try_now()
	tabs.Set("currentIndex", tabs.Property("count").(int)-1)
}

// called when a view is closed
func (t *qmlfrontend) onClose(v *backend.View) {
	w2 := t.windows[v.Window()]
	for i := range w2.views {
		if w2.views[i].bv == v {
			w2.window.ObjectByName("tabs").Call("removeTab", i)
			copy(w2.views[i:], w2.views[i+1:])
			w2.views = w2.views[:len(w2.views)-1]
			return
		}
	}
	log.Error("Couldn't find closed view...")
}

// called when a view has loaded
func (t *qmlfrontend) onLoad(v *backend.View) {
	w2 := t.windows[v.Window()]
	i := 0
	for i = range w2.views {
		if w2.views[i].bv == v {
			break
		}
	}
	v2 := w2.views[i]
	v2.Title.Text = v.Buffer().FileName()
	tabs := w2.window.ObjectByName("tabs")
	tabs.Set("currentIndex", w2.ActiveViewIndex())
	tab := tabs.Call("getTab", i).(qml.Object)
	tab.Set("title", v2.Title.Text)
}

func (t *qmlfrontend) onSelectionModified(v *backend.View) {
	w2 := t.windows[v.Window()]
	i := 0
	for i = range w2.views {
		if w2.views[i].bv == v {
			break
		}
	}
	v2 := w2.views[i]
	v2.qv.Call("onSelectionModified")
}

// Launches the provided command in a new goroutine
// (to avoid locking up the GUI)
func (t *qmlfrontend) RunCommand(command string) {
	t.RunCommandWithArgs(command, make(backend.Args))
}

func (t *qmlfrontend) RunCommandWithArgs(command string, args backend.Args) {
	ed := backend.GetEditor()
	go ed.RunCommand(command, args)
}

func (t *qmlfrontend) HandleInput(text string, keycode int, modifiers int) bool {
	log.Debug("qmlfrontend.HandleInput: text=%v, key=%x, modifiers=%x", text, keycode, modifiers)
	shift := false
	alt := false
	ctrl := false
	super := false

	if key, ok := lut[keycode]; ok {
		ed := backend.GetEditor()

		if (modifiers & shift_mod) != 0 {
			shift = true
		}
		if (modifiers & alt_mod) != 0 {
			alt = true
		}
		if (modifiers & ctrl_mod) != 0 {
			if runtime.GOOS == "darwin" {
				super = true
			} else {
				ctrl = true
			}
		}
		if (modifiers & meta_mod) != 0 {
			if runtime.GOOS == "darwin" {
				ctrl = true
			} else {
				super = true
			}
		}

		ed.HandleInput(keys.KeyPress{Text: text, Key: key, Shift: shift, Alt: alt, Ctrl: ctrl, Super: super})
		return true
	}
	return false
}

// Quit closes all open windows to de-reference all qml objects
func (t *qmlfrontend) Quit() (err error) {
	// todo: handle changed files that aren't saved.
	for _, v := range t.windows {
		if v.window != nil {
			v.window.Hide()
			v.window.Destroy()
			v.window = nil
		}
	}
	return
}

func (t *qmlfrontend) loop() (err error) {
	backend.OnNew.Add(t.onNew)
	backend.OnClose.Add(t.onClose)
	backend.OnLoad.Add(t.onLoad)
	backend.OnSelectionModified.Add(t.onSelectionModified)

	ed := backend.GetEditor()
	ed.SetFrontend(t)
	ed.LogInput(false)
	ed.LogCommands(false)
	c := ed.Console()
	t.Console = &frontendView{bv: c}
	c.Buffer().AddObserver(t.Console)
	c.Buffer().AddObserver(t)

	var (
		engine    *qml.Engine
		component qml.Object
		// WaitGroup keeping track of open windows
		wg sync.WaitGroup
	)

	// create and setup a new engine, destroying
	// the old one if one exists.
	//
	// This is needed to re-load qml files to get
	// the new file contents from disc as otherwise
	// the old file would still be what is referenced.
	newEngine := func() (err error) {
		if engine != nil {
			log.Debug("calling destroy")
			// TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now
			// engine.Destroy()
			engine = nil
		}
		log.Debug("calling newEngine")
		engine = qml.NewEngine()
		engine.On("quit", t.Quit)
		log.Debug("setvar frontend")
		engine.Context().SetVar("frontend", t)
		log.Debug("setvar editor")
		engine.Context().SetVar("editor", backend.GetEditor())

		log.Debug("loadfile")
		component, err = engine.LoadFile(qmlMainFile)
		if err != nil {
			return err
		}
		limeViewComponent, err = engine.LoadFile(qmlViewFile)
		return
	}
	if err := newEngine(); err != nil {
		log.Error(err)
	}

	backend.OnNewWindow.Add(func(w *backend.Window) {
		fw := &frontendWindow{bw: w}
		t.windows[w] = fw
		if component != nil {
			fw.launch(&wg, component)
		}
	})

	// TODO: should be done backend side
	if sc, err := textmate.LoadTheme("../packages/themes/TextMate-Themes/Monokai.tmTheme"); err != nil {
		log.Error(err)
	} else {
		scheme = sc
	}

	// TODO: setting syntax should be done automaticly in backend and after
	// implementing this we could run Init in a go routine and remove the
	// next two line
	ed.Init()
	v := ed.ActiveWindow().OpenFile("main.go", 0)
	v.SetSyntaxFile("../packages/go.tmbundle/Syntaxes/Go.tmLanguage")

	defer func() {
		fmt.Println(util.Prof)
	}()

	// The rest of code is related to livereloading qml files
	// TODO: this doesnt work currently
	watch, err := fsnotify.NewWatcher()
	if err != nil {
		log.Error("Unable to create file watcher: %s", err)
		return
	}
	defer watch.Close()
	watch.Watch(".")
	defer watch.RemoveWatch(".")

	reloadRequested := false

	go func() {
		for {
			select {
			case ev := <-watch.Event:
				if ev != nil && strings.HasSuffix(ev.Name, ".qml") && ev.IsModify() && !ev.IsAttrib() {
					reloadRequested = true
					t.Quit()
				}
			}
		}
	}()

	for {
		// Reset reload status
		reloadRequested = false

		log.Debug("Waiting for all windows to close")
		// wg would be the WaitGroup all windows belong to, so first we wait for
		// all windows to close.
		wg.Wait()
		log.Debug("All windows closed. reloadRequest: %v", reloadRequested)
		// then we check if there's a reload request in the pipe
		if !reloadRequested || len(t.windows) == 0 {
			// This would be a genuine exit; all windows closed by the user
			break
		}

		// *We* closed all windows because we want to reload freshly changed qml
		// files.
		for {
			log.Debug("Calling newEngine")
			if err := newEngine(); err != nil {
				// Reset reload status
				reloadRequested = false
				log.Error(err)
				for !reloadRequested {
					// This loop allows us to re-try reloading
					// if there was an error in the file this time,
					// we just loop around again when we receive the next
					// reload request (ie on the next save of the file).
					time.Sleep(time.Second)
				}
				continue
			}
			log.Debug("break")
			break
		}
		log.Debug("re-launching all windows")
		// Succeeded loading the file, re-launch all windows
		for _, v := range t.windows {
			v.launch(&wg, component)
		}
	}

	return
}
<|endoftext|>


<commit_before>package rabbitsmpp

import (
	"encoding/json"
	"errors"
	"strconv"
	"sync"

	"github.com/cenk/backoff"
	"github.com/streadway/amqp"
)

var (
	errAlreadyBound = errors.New("already bound")
	errClientClosed = errors.New("closed client")
	errMaxBindretry = errors.New("max bind retry")
)

const (
	defaultRebindTry = 5
)

// PublisherClient is same as Client interface but also support Rebind operation
type PublisherClient interface {
	Client
	ReBind() (PublisherClient, chan *amqp.Error, error)
}

type publisherClient struct {
	sm *sync.Mutex

	config   Config
	ampqConn *amqp.Connection
	bound    bool

	closed bool
}

func newPublisherClient(conf Config) (*publisherClient, error) {
	var m sync.Mutex

	return &publisherClient{
		sm:     &m,
		config: conf,
	}, nil
}

func (c *publisherClient) Bind() (chan *amqp.Error, error) {
	c.sm.Lock()
	defer c.sm.Unlock()

	if c.bound {
		return nil, errAlreadyBound
	}

	if c.closed {
		return nil, errClientClosed
	}

	conn, err := amqp.Dial(c.config.URL)
	if err != nil {
		return nil, err
	}

	errors := make(chan *amqp.Error)
	errChan := conn.NotifyClose(errors)
	c.ampqConn = conn

	c.bound = true
	return errChan, nil
}

func (c *publisherClient) Channel() (Channel, error) {
	c.sm.Lock()
	defer c.sm.Unlock()

	if c.closed {
		return nil, errClientClosed
	}

	return c.ampqConn.Channel()
}

func (c *publisherClient) ReBind() (PublisherClient, chan *amqp.Error, error) {
	c.sm.Lock()
	defer c.sm.Unlock()

	ticker := backoff.NewTicker(backoff.NewExponentialBackOff())
	for _ = range ticker.C {
		newClient, err := newPublisherClient(c.config)
		if err != nil {
			continue
		}

		errChan, err := newClient.Bind()
		if err != nil {
			continue
		}

		// stop the ticker
		ticker.Stop()
		return newClient, errChan, nil
	}

	return nil, nil, errClientClosed

}

func (c *publisherClient) Close() error {
	c.sm.Lock()
	defer c.sm.Unlock()

	if c.closed {
		return nil
	}

	c.closed = true
	c.ampqConn.Close()
	return nil
}

func (c *publisherClient) QueueName() string {
	c.sm.Lock()
	defer c.sm.Unlock()

	return c.config.QueueName
}

// Publisher is interface for publishing data to the queues
type Publisher interface {
	Publish(Job) error
	Close() error
}

func runWithRecovery(runCmd func() error, onError func() error) error {
	if err := runCmd(); err == nil {
		return nil
	}

	if err := onError(); err != nil {
		return err
	}

	return runCmd()

}

type publisher struct {
	m        *sync.Mutex
	client   PublisherClient
	sendChan Channel
}

// NewPublisher creates a new publisher with direct exchange
func NewPublisher(conf Config) (Publisher, error) {
	pubClient, err := newPublisherClient(conf)
	if err != nil {
		return nil, err
	}

	return newPublisherWithClient(pubClient)
}

func newPublisherWithClient(client PublisherClient) (Publisher, error) {
	var m sync.Mutex

	publisher := &publisher{
		m:      &m,
		client: client,
	}

	// setup all the connections here
	if err := publisher.reset(false); err != nil {
		return nil, err
	}

	return publisher, nil
}

func (p *publisher) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	p.sendChan.Close()
	p.client.Close()
	return nil
}

func (p *publisher) queueSetup() error {
	ch, err := p.client.Channel()
	if err != nil {
		p.Close()
		return err
	}

	p.sendChan = ch

	_, err = ch.QueueDeclare(
		p.client.QueueName(), // name
		true,                 // durable
		false,                // delete when unused
		false,                // exclusive
		false,                // no-wait
		nil,                  // arguments
	)
	if err != nil {
		p.Close()
		return err
	}

	return nil
}

// Publish sends the Job to the rabbitmq if the underlying connection needs re-binding it is done automagically
func (p *publisher) Publish(j Job) error {
	p.m.Lock()
	defer p.m.Unlock()

	bodyBytes, err := json.Marshal(j)
	if err != nil {
		return err
	}

	cmd := func() error {
		return p.sendChan.Publish(
			"",                   // exchange
			p.client.QueueName(), // routing key
			false,                // mandatory
			false,                // immediate
			amqp.Publishing{
				ContentType: "application/json",
				Body:        bodyBytes,
			})
	}

	return runWithRecovery(cmd, func() error {
		return p.reset(true)
	})
}

func (p *publisher) reset(rebind bool) error {

	if !rebind {
		_, err := p.client.Bind()
		if err != nil {
			return err
		}
	} else {
		client, _, err := p.client.ReBind()
		if err != nil {
			return err
		}

		p.client = client
	}

	// setup the queues
	return p.queueSetup()
}

type delayedPublisher struct {
	m        sync.Mutex
	client   PublisherClient
	sendChan Channel

	delayExchange string
	delayMS       int
}

// NewDelayedPublisher works the same way as the normal publisher except it pushes
// its messages are sent to a delayExchange which keeps them there for delayMS time and then redirects
// them to the conf.QueueName so consumers can start consuming them
func NewDelayedPublisher(conf Config, delayExchange string, delayMS int) (Publisher, error) {
	client, err := newPublisherClient(conf)
	if err != nil {
		return nil, err
	}

	return newDelayedPublisherWithClient(client, delayExchange, delayMS)
}

func newDelayedPublisherWithClient(client PublisherClient, delayExchange string, delayMS int) (Publisher, error) {
	var m sync.Mutex

	publisher := &delayedPublisher{
		m:             m,
		client:        client,
		delayExchange: delayExchange,
		delayMS:       delayMS,
	}

	// setup all the connections here
	if err := publisher.reset(false); err != nil {
		return nil, err
	}

	return publisher, nil
}

func (p *delayedPublisher) reset(rebind bool) error {

	if !rebind {
		_, err := p.client.Bind()
		if err != nil {
			return err
		}
	} else {
		client, _, err := p.client.ReBind()
		if err != nil {
			return err
		}

		p.client = client
	}

	// setup the queues
	if err := p.queueSetup(); err != nil {
		return err
	}

	return nil
}

func (p *delayedPublisher) queueSetup() error {
	ch, err := p.client.Channel()
	if err != nil {
		p.Close()
		return err
	}

	p.sendChan = ch

	// Create a delayed exchange
	err = ch.ExchangeDeclare(
		p.delayExchange,
		"x-delayed-message",
		true,
		false,
		false,
		true,
		amqp.Table{
			"x-delayed-type": "direct",
		},
	)

	if err != nil {
		return err
	}

	q, err := ch.QueueDeclare(
		p.client.QueueName(), // name
		true,                 // durable
		false,                // delete when unused
		false,                // exclusive
		false,                // no-wait
		nil,                  // arguments
	)
	if err != nil {
		return err
	}

	err = ch.QueueBind(
		q.Name,               // queue name
		p.client.QueueName(), // routing key
		p.delayExchange,      // exchange
		false,
		nil)

	if err != nil {
		return err
	}

	return nil
}

func (p *delayedPublisher) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	p.sendChan.Close()
	p.client.Close()
	return nil
}

func (p *delayedPublisher) Publish(j Job) error {
	p.m.Lock()
	defer p.m.Unlock()

	bodyBytes, err := json.Marshal(j)
	if err != nil {
		return err
	}

	cmd := func() error {
		return p.sendChan.Publish(
			p.delayExchange,      // exchange
			p.client.QueueName(), // routing key
			false,                // mandatory
			false,                // immediate
			amqp.Publishing{
				Headers: amqp.Table{
					"x-delay": strconv.Itoa(p.delayMS),
				},
				ContentType: "application/json",
				Body:        bodyBytes,
			})
	}

	return runWithRecovery(cmd, func() error {
		return p.reset(true)
	})
}
<commit_msg>rm defaultRetry backoff package has good defaults<commit_after>package rabbitsmpp

import (
	"encoding/json"
	"errors"
	"strconv"
	"sync"

	"github.com/cenk/backoff"
	"github.com/streadway/amqp"
)

var (
	errAlreadyBound = errors.New("already bound")
	errClientClosed = errors.New("closed client")
	errMaxBindretry = errors.New("max bind retry")
)

// PublisherClient is same as Client interface but also support Rebind operation
type PublisherClient interface {
	Client
	ReBind() (PublisherClient, chan *amqp.Error, error)
}

type publisherClient struct {
	sm *sync.Mutex

	config   Config
	ampqConn *amqp.Connection
	bound    bool

	closed bool
}

func newPublisherClient(conf Config) (*publisherClient, error) {
	var m sync.Mutex

	return &publisherClient{
		sm:     &m,
		config: conf,
	}, nil
}

func (c *publisherClient) Bind() (chan *amqp.Error, error) {
	c.sm.Lock()
	defer c.sm.Unlock()

	if c.bound {
		return nil, errAlreadyBound
	}

	if c.closed {
		return nil, errClientClosed
	}

	conn, err := amqp.Dial(c.config.URL)
	if err != nil {
		return nil, err
	}

	errors := make(chan *amqp.Error)
	errChan := conn.NotifyClose(errors)
	c.ampqConn = conn

	c.bound = true
	return errChan, nil
}

func (c *publisherClient) Channel() (Channel, error) {
	c.sm.Lock()
	defer c.sm.Unlock()

	if c.closed {
		return nil, errClientClosed
	}

	return c.ampqConn.Channel()
}

func (c *publisherClient) ReBind() (PublisherClient, chan *amqp.Error, error) {
	c.sm.Lock()
	defer c.sm.Unlock()

	ticker := backoff.NewTicker(backoff.NewExponentialBackOff())
	for _ = range ticker.C {
		newClient, err := newPublisherClient(c.config)
		if err != nil {
			continue
		}

		errChan, err := newClient.Bind()
		if err != nil {
			continue
		}

		// stop the ticker
		ticker.Stop()
		return newClient, errChan, nil
	}

	return nil, nil, errClientClosed

}

func (c *publisherClient) Close() error {
	c.sm.Lock()
	defer c.sm.Unlock()

	if c.closed {
		return nil
	}

	c.closed = true
	c.ampqConn.Close()
	return nil
}

func (c *publisherClient) QueueName() string {
	c.sm.Lock()
	defer c.sm.Unlock()

	return c.config.QueueName
}

// Publisher is interface for publishing data to the queues
type Publisher interface {
	Publish(Job) error
	Close() error
}

func runWithRecovery(runCmd func() error, onError func() error) error {
	if err := runCmd(); err == nil {
		return nil
	}

	if err := onError(); err != nil {
		return err
	}

	return runCmd()

}

type publisher struct {
	m        *sync.Mutex
	client   PublisherClient
	sendChan Channel
}

// NewPublisher creates a new publisher with direct exchange
func NewPublisher(conf Config) (Publisher, error) {
	pubClient, err := newPublisherClient(conf)
	if err != nil {
		return nil, err
	}

	return newPublisherWithClient(pubClient)
}

func newPublisherWithClient(client PublisherClient) (Publisher, error) {
	var m sync.Mutex

	publisher := &publisher{
		m:      &m,
		client: client,
	}

	// setup all the connections here
	if err := publisher.reset(false); err != nil {
		return nil, err
	}

	return publisher, nil
}

func (p *publisher) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	p.sendChan.Close()
	p.client.Close()
	return nil
}

func (p *publisher) queueSetup() error {
	ch, err := p.client.Channel()
	if err != nil {
		p.Close()
		return err
	}

	p.sendChan = ch

	_, err = ch.QueueDeclare(
		p.client.QueueName(), // name
		true,                 // durable
		false,                // delete when unused
		false,                // exclusive
		false,                // no-wait
		nil,                  // arguments
	)
	if err != nil {
		p.Close()
		return err
	}

	return nil
}

// Publish sends the Job to the rabbitmq if the underlying connection needs re-binding it is done automagically
func (p *publisher) Publish(j Job) error {
	p.m.Lock()
	defer p.m.Unlock()

	bodyBytes, err := json.Marshal(j)
	if err != nil {
		return err
	}

	cmd := func() error {
		return p.sendChan.Publish(
			"",                   // exchange
			p.client.QueueName(), // routing key
			false,                // mandatory
			false,                // immediate
			amqp.Publishing{
				ContentType: "application/json",
				Body:        bodyBytes,
			})
	}

	return runWithRecovery(cmd, func() error {
		return p.reset(true)
	})
}

func (p *publisher) reset(rebind bool) error {

	if !rebind {
		_, err := p.client.Bind()
		if err != nil {
			return err
		}
	} else {
		client, _, err := p.client.ReBind()
		if err != nil {
			return err
		}

		p.client = client
	}

	// setup the queues
	return p.queueSetup()
}

type delayedPublisher struct {
	m        sync.Mutex
	client   PublisherClient
	sendChan Channel

	delayExchange string
	delayMS       int
}

// NewDelayedPublisher works the same way as the normal publisher except it pushes
// its messages are sent to a delayExchange which keeps them there for delayMS time and then redirects
// them to the conf.QueueName so consumers can start consuming them
func NewDelayedPublisher(conf Config, delayExchange string, delayMS int) (Publisher, error) {
	client, err := newPublisherClient(conf)
	if err != nil {
		return nil, err
	}

	return newDelayedPublisherWithClient(client, delayExchange, delayMS)
}

func newDelayedPublisherWithClient(client PublisherClient, delayExchange string, delayMS int) (Publisher, error) {
	var m sync.Mutex

	publisher := &delayedPublisher{
		m:             m,
		client:        client,
		delayExchange: delayExchange,
		delayMS:       delayMS,
	}

	// setup all the connections here
	if err := publisher.reset(false); err != nil {
		return nil, err
	}

	return publisher, nil
}

func (p *delayedPublisher) reset(rebind bool) error {

	if !rebind {
		_, err := p.client.Bind()
		if err != nil {
			return err
		}
	} else {
		client, _, err := p.client.ReBind()
		if err != nil {
			return err
		}

		p.client = client
	}

	// setup the queues
	if err := p.queueSetup(); err != nil {
		return err
	}

	return nil
}

func (p *delayedPublisher) queueSetup() error {
	ch, err := p.client.Channel()
	if err != nil {
		p.Close()
		return err
	}

	p.sendChan = ch

	// Create a delayed exchange
	err = ch.ExchangeDeclare(
		p.delayExchange,
		"x-delayed-message",
		true,
		false,
		false,
		true,
		amqp.Table{
			"x-delayed-type": "direct",
		},
	)

	if err != nil {
		return err
	}

	q, err := ch.QueueDeclare(
		p.client.QueueName(), // name
		true,                 // durable
		false,                // delete when unused
		false,                // exclusive
		false,                // no-wait
		nil,                  // arguments
	)
	if err != nil {
		return err
	}

	err = ch.QueueBind(
		q.Name,               // queue name
		p.client.QueueName(), // routing key
		p.delayExchange,      // exchange
		false,
		nil)

	if err != nil {
		return err
	}

	return nil
}

func (p *delayedPublisher) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	p.sendChan.Close()
	p.client.Close()
	return nil
}

func (p *delayedPublisher) Publish(j Job) error {
	p.m.Lock()
	defer p.m.Unlock()

	bodyBytes, err := json.Marshal(j)
	if err != nil {
		return err
	}

	cmd := func() error {
		return p.sendChan.Publish(
			p.delayExchange,      // exchange
			p.client.QueueName(), // routing key
			false,                // mandatory
			false,                // immediate
			amqp.Publishing{
				Headers: amqp.Table{
					"x-delay": strconv.Itoa(p.delayMS),
				},
				ContentType: "application/json",
				Body:        bodyBytes,
			})
	}

	return runWithRecovery(cmd, func() error {
		return p.reset(true)
	})
}
<|endoftext|>


<commit_before>package mysql

import (
	"context"
	"testing"
)

func TestAll(t *testing.T) {
	ctx := context.Background()

	const dsn = "root:example@tcp(127.0.0.1:4406)/repodb"
	db, err := ConnectDB(ctx, dsn)
	if err != nil {
		t.Fatal(err)
	}

	createTableIfNotExists(t, ctx, db)

	defer func() {
		dropTable(t, ctx, db)
		err = db.Close()
		if err != nil {
			t.Fatalf("close: %v", err)
		}
	}()
}

func createTableIfNotExists(t *testing.T, ctx context.Context, db *DB) {
	t.Helper()
	const query = `CREATE TABLE IF NOT EXISTS repotable (
    				id INT AUTO_INCREMENT PRIMARY KEY,
    				title VARCHAR(255) NOT NULL,
    				description TEXT NOT NULL,
    				priority TINYINT,
    				date_of_birth TIMESTAMP,
    				created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
			)`

	_, err := db.db.Exec(query)
	if err != nil {
		t.Fatal(err)
	}
}

func dropTable(t *testing.T, ctx context.Context, db *DB) {
	t.Helper()
	const query = `DROP TABLE repotable`

	_, err := db.db.Exec(query)
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>repo/mysql: add setupAndCleanup<commit_after>package mysql

import (
	"context"
	"testing"
)

func TestAll(t *testing.T) {
	const dsn = "root:example@tcp(127.0.0.1:4406)/repodb"

	ctx, db := setupAndCleanup(t, dsn)

	_, _ = ctx, db
}

func setupAndCleanup(t *testing.T, dsn string) (context.Context, *DB) {
	t.Helper()

	ctx := context.Background()

	db, err := ConnectDB(ctx, dsn)
	if err != nil {
		t.Fatal(err)
	}

	createTableIfNotExists(t, ctx, db)

	t.Cleanup(func() {
		dropTable(t, ctx, db)
		err := db.Close()
		if err != nil {
			t.Fatalf("close: %v", err)
		}
	})

	return ctx, db
}

func createTableIfNotExists(t *testing.T, ctx context.Context, db *DB) {
	t.Helper()
	const query = `CREATE TABLE IF NOT EXISTS repotable (
    				id INT AUTO_INCREMENT PRIMARY KEY,
    				title VARCHAR(255) NOT NULL,
    				description TEXT NOT NULL,
    				priority TINYINT,
    				date_of_birth TIMESTAMP,
    				created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
			)`

	_, err := db.db.Exec(query)
	if err != nil {
		t.Fatal(err)
	}
}

func dropTable(t *testing.T, ctx context.Context, db *DB) {
	t.Helper()
	const query = `DROP TABLE repotable`

	_, err := db.db.Exec(query)
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>