<commit_before>/*
Copyright 2018 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 stats

import (
	"fmt"
	"path/filepath"

	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
	"k8s.io/kubernetes/pkg/volume"
)

// PodEtcHostsPathFunc is a function to fetch a etc hosts path by pod uid and whether etc host path is supported by the runtime
type PodEtcHostsPathFunc func(podUID types.UID) (string, bool)

// metricsProviderByPath maps a path to its metrics provider
type metricsProviderByPath map[string]volume.MetricsProvider

// HostStatsProvider defines an interface for providing host stats associated with pod.
type HostStatsProvider interface {
	// getPodLogStats gets stats associated with pod log usage
	getPodLogStats(podNamespace, podName string, podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error)
	// getPodContainerLogStats gets stats associated with container log usage
	getPodContainerLogStats(podNamespace, podName string, podUID types.UID, containerName string, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error)
	// getPodEtcHostsStats gets stats associated with pod etc-hosts usage
	getPodEtcHostsStats(podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error)
}

type hostStatsProvider struct {
	// osInterface is the interface for syscalls.
	osInterface kubecontainer.OSInterface
	// podEtcHostsPathFunc fetches a pod etc hosts path by uid.
	podEtcHostsPathFunc PodEtcHostsPathFunc
}

// NewHostStatsProvider returns a new HostStatsProvider type struct.
func NewHostStatsProvider(osInterface kubecontainer.OSInterface, podEtcHostsPathFunc PodEtcHostsPathFunc) HostStatsProvider {
	return hostStatsProvider{
		osInterface:         osInterface,
		podEtcHostsPathFunc: podEtcHostsPathFunc,
	}
}

func (h hostStatsProvider) getPodLogStats(podNamespace, podName string, podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	metricsByPath, err := h.podLogMetrics(podNamespace, podName, podUID)
	if err != nil {
		return nil, err
	}
	return metricsByPathToFsStats(metricsByPath, rootFsInfo)
}

// getPodContainerLogStats gets stats for container
func (h hostStatsProvider) getPodContainerLogStats(podNamespace, podName string, podUID types.UID, containerName string, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	metricsByPath, err := h.podContainerLogMetrics(podNamespace, podName, podUID, containerName)
	if err != nil {
		return nil, err
	}
	return metricsByPathToFsStats(metricsByPath, rootFsInfo)
}

// getPodEtcHostsStats gets status for pod etc hosts usage
func (h hostStatsProvider) getPodEtcHostsStats(podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	// Runtimes may not support etc hosts file (Windows with docker)
	podEtcHostsPath, isEtcHostsSupported := h.podEtcHostsPathFunc(podUID)
	if !isEtcHostsSupported {
		return nil, nil
	}

	metrics := volume.NewMetricsDu(podEtcHostsPath)
	hostMetrics, err := metrics.GetMetrics()
	if err != nil {
		return nil, fmt.Errorf("failed to get stats %v", err)
	}
	result := rootFsInfoToFsStats(rootFsInfo)
	usedBytes := uint64(hostMetrics.Used.Value())
	inodesUsed := uint64(hostMetrics.InodesUsed.Value())
	result.UsedBytes = addUsage(result.UsedBytes, &usedBytes)
	result.InodesUsed = addUsage(result.InodesUsed, &inodesUsed)
	result.Time = maxUpdateTime(&result.Time, &hostMetrics.Time)
	return result, nil
}

func (h hostStatsProvider) podLogMetrics(podNamespace, podName string, podUID types.UID) (metricsProviderByPath, error) {
	podLogsDirectoryPath := kuberuntime.BuildPodLogsDirectory(podNamespace, podName, podUID)
	return h.fileMetricsByDir(podLogsDirectoryPath)
}

func (h hostStatsProvider) podContainerLogMetrics(podNamespace, podName string, podUID types.UID, containerName string) (metricsProviderByPath, error) {
	podContainerLogsDirectoryPath := kuberuntime.BuildContainerLogsDirectory(podNamespace, podName, podUID, containerName)
	return h.fileMetricsByDir(podContainerLogsDirectoryPath)
}

// fileMetricsByDir returns metrics by path for each file under specified directory
func (h hostStatsProvider) fileMetricsByDir(dirname string) (metricsProviderByPath, error) {
	files, err := h.osInterface.ReadDir(dirname)
	if err != nil {
		return nil, err
	}
	results := metricsProviderByPath{}
	for _, f := range files {
		if f.IsDir() {
			continue
		}
		// Only include *files* under pod log directory.
		fpath := filepath.Join(dirname, f.Name())
		results[fpath] = volume.NewMetricsDu(fpath)
	}
	return results, nil
}

// metricsByPathToFsStats converts a metrics provider by path to fs stats
func metricsByPathToFsStats(metricsByPath metricsProviderByPath, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	result := rootFsInfoToFsStats(rootFsInfo)
	for fpath, metrics := range metricsByPath {
		hostMetrics, err := metrics.GetMetrics()
		if err != nil {
			return nil, fmt.Errorf("failed to get fsstats for %q: %v", fpath, err)
		}
		usedBytes := uint64(hostMetrics.Used.Value())
		inodesUsed := uint64(hostMetrics.InodesUsed.Value())
		result.UsedBytes = addUsage(result.UsedBytes, &usedBytes)
		result.InodesUsed = addUsage(result.InodesUsed, &inodesUsed)
		result.Time = maxUpdateTime(&result.Time, &hostMetrics.Time)
	}
	return result, nil
}

// rootFsInfoToFsStats is a utility to convert rootFsInfo into statsapi.FsStats
func rootFsInfoToFsStats(rootFsInfo *cadvisorapiv2.FsInfo) *statsapi.FsStats {
	return &statsapi.FsStats{
		Time:           metav1.NewTime(rootFsInfo.Timestamp),
		AvailableBytes: &rootFsInfo.Available,
		CapacityBytes:  &rootFsInfo.Capacity,
		InodesFree:     rootFsInfo.InodesFree,
		Inodes:         rootFsInfo.Inodes,
	}
}
<commit_msg>UPSTREAM: 101708: Fix log spam for du failure on pod etc-hosts metrics<commit_after>/*
Copyright 2018 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 stats

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

	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
	"k8s.io/kubernetes/pkg/volume"
)

// PodEtcHostsPathFunc is a function to fetch a etc hosts path by pod uid and whether etc host path is supported by the runtime
type PodEtcHostsPathFunc func(podUID types.UID) (string, bool)

// metricsProviderByPath maps a path to its metrics provider
type metricsProviderByPath map[string]volume.MetricsProvider

// HostStatsProvider defines an interface for providing host stats associated with pod.
type HostStatsProvider interface {
	// getPodLogStats gets stats associated with pod log usage
	getPodLogStats(podNamespace, podName string, podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error)
	// getPodContainerLogStats gets stats associated with container log usage
	getPodContainerLogStats(podNamespace, podName string, podUID types.UID, containerName string, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error)
	// getPodEtcHostsStats gets stats associated with pod etc-hosts usage
	getPodEtcHostsStats(podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error)
}

type hostStatsProvider struct {
	// osInterface is the interface for syscalls.
	osInterface kubecontainer.OSInterface
	// podEtcHostsPathFunc fetches a pod etc hosts path by uid.
	podEtcHostsPathFunc PodEtcHostsPathFunc
}

// NewHostStatsProvider returns a new HostStatsProvider type struct.
func NewHostStatsProvider(osInterface kubecontainer.OSInterface, podEtcHostsPathFunc PodEtcHostsPathFunc) HostStatsProvider {
	return hostStatsProvider{
		osInterface:         osInterface,
		podEtcHostsPathFunc: podEtcHostsPathFunc,
	}
}

func (h hostStatsProvider) getPodLogStats(podNamespace, podName string, podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	metricsByPath, err := h.podLogMetrics(podNamespace, podName, podUID)
	if err != nil {
		return nil, err
	}
	return metricsByPathToFsStats(metricsByPath, rootFsInfo)
}

// getPodContainerLogStats gets stats for container
func (h hostStatsProvider) getPodContainerLogStats(podNamespace, podName string, podUID types.UID, containerName string, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	metricsByPath, err := h.podContainerLogMetrics(podNamespace, podName, podUID, containerName)
	if err != nil {
		return nil, err
	}
	return metricsByPathToFsStats(metricsByPath, rootFsInfo)
}

// getPodEtcHostsStats gets status for pod etc hosts usage
func (h hostStatsProvider) getPodEtcHostsStats(podUID types.UID, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	// Runtimes may not support etc hosts file (Windows with docker)
	podEtcHostsPath, isEtcHostsSupported := h.podEtcHostsPathFunc(podUID)
	if !isEtcHostsSupported {
		return nil, nil
	}
	// Some pods have an explicit /etc/hosts mount and the Kubelet will not create an etc-hosts file for them
	if _, err := os.Stat(podEtcHostsPath); os.IsNotExist(err) {
		return nil, nil
	}

	metrics := volume.NewMetricsDu(podEtcHostsPath)
	hostMetrics, err := metrics.GetMetrics()
	if err != nil {
		return nil, fmt.Errorf("failed to get stats %v", err)
	}
	result := rootFsInfoToFsStats(rootFsInfo)
	usedBytes := uint64(hostMetrics.Used.Value())
	inodesUsed := uint64(hostMetrics.InodesUsed.Value())
	result.UsedBytes = addUsage(result.UsedBytes, &usedBytes)
	result.InodesUsed = addUsage(result.InodesUsed, &inodesUsed)
	result.Time = maxUpdateTime(&result.Time, &hostMetrics.Time)
	return result, nil
}

func (h hostStatsProvider) podLogMetrics(podNamespace, podName string, podUID types.UID) (metricsProviderByPath, error) {
	podLogsDirectoryPath := kuberuntime.BuildPodLogsDirectory(podNamespace, podName, podUID)
	return h.fileMetricsByDir(podLogsDirectoryPath)
}

func (h hostStatsProvider) podContainerLogMetrics(podNamespace, podName string, podUID types.UID, containerName string) (metricsProviderByPath, error) {
	podContainerLogsDirectoryPath := kuberuntime.BuildContainerLogsDirectory(podNamespace, podName, podUID, containerName)
	return h.fileMetricsByDir(podContainerLogsDirectoryPath)
}

// fileMetricsByDir returns metrics by path for each file under specified directory
func (h hostStatsProvider) fileMetricsByDir(dirname string) (metricsProviderByPath, error) {
	files, err := h.osInterface.ReadDir(dirname)
	if err != nil {
		return nil, err
	}
	results := metricsProviderByPath{}
	for _, f := range files {
		if f.IsDir() {
			continue
		}
		// Only include *files* under pod log directory.
		fpath := filepath.Join(dirname, f.Name())
		results[fpath] = volume.NewMetricsDu(fpath)
	}
	return results, nil
}

// metricsByPathToFsStats converts a metrics provider by path to fs stats
func metricsByPathToFsStats(metricsByPath metricsProviderByPath, rootFsInfo *cadvisorapiv2.FsInfo) (*statsapi.FsStats, error) {
	result := rootFsInfoToFsStats(rootFsInfo)
	for fpath, metrics := range metricsByPath {
		hostMetrics, err := metrics.GetMetrics()
		if err != nil {
			return nil, fmt.Errorf("failed to get fsstats for %q: %v", fpath, err)
		}
		usedBytes := uint64(hostMetrics.Used.Value())
		inodesUsed := uint64(hostMetrics.InodesUsed.Value())
		result.UsedBytes = addUsage(result.UsedBytes, &usedBytes)
		result.InodesUsed = addUsage(result.InodesUsed, &inodesUsed)
		result.Time = maxUpdateTime(&result.Time, &hostMetrics.Time)
	}
	return result, nil
}

// rootFsInfoToFsStats is a utility to convert rootFsInfo into statsapi.FsStats
func rootFsInfoToFsStats(rootFsInfo *cadvisorapiv2.FsInfo) *statsapi.FsStats {
	return &statsapi.FsStats{
		Time:           metav1.NewTime(rootFsInfo.Timestamp),
		AvailableBytes: &rootFsInfo.Available,
		CapacityBytes:  &rootFsInfo.Capacity,
		InodesFree:     rootFsInfo.InodesFree,
		Inodes:         rootFsInfo.Inodes,
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The gVisor 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.

// +build linux

// Package rawfile contains utilities for using the netstack with raw host
// files on Linux hosts.
package rawfile

import (
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
	"gvisor.dev/gvisor/pkg/tcpip"
)

// GetMTU determines the MTU of a network interface device.
func GetMTU(name string) (uint32, error) {
	fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
	if err != nil {
		return 0, err
	}

	defer syscall.Close(fd)

	var ifreq struct {
		name [16]byte
		mtu  int32
		_    [20]byte
	}

	copy(ifreq.name[:], name)
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
	if errno != 0 {
		return 0, errno
	}

	return uint32(ifreq.mtu), nil
}

// NonBlockingWrite writes the given buffer to a file descriptor. It fails if
// partial data is written.
func NonBlockingWrite(fd int, buf []byte) *tcpip.Error {
	var ptr unsafe.Pointer
	if len(buf) > 0 {
		ptr = unsafe.Pointer(&buf[0])
	}

	_, _, e := syscall.RawSyscall(syscall.SYS_WRITE, uintptr(fd), uintptr(ptr), uintptr(len(buf)))
	if e != 0 {
		return TranslateErrno(e)
	}

	return nil
}

// NonBlockingWrite3 writes up to three byte slices to a file descriptor in a
// single syscall. It fails if partial data is written.
func NonBlockingWrite3(fd int, b1, b2, b3 []byte) *tcpip.Error {
	// If the is no second buffer, issue a regular write.
	if len(b2) == 0 {
		return NonBlockingWrite(fd, b1)
	}

	// We have two buffers. Build the iovec that represents them and issue
	// a writev syscall.
	iovec := [3]syscall.Iovec{
		{
			Base: &b1[0],
			Len:  uint64(len(b1)),
		},
		{
			Base: &b2[0],
			Len:  uint64(len(b2)),
		},
	}
	iovecLen := uintptr(2)

	if len(b3) > 0 {
		iovecLen++
		iovec[2].Base = &b3[0]
		iovec[2].Len = uint64(len(b3))
	}

	_, _, e := syscall.RawSyscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovec[0])), iovecLen)
	if e != 0 {
		return TranslateErrno(e)
	}

	return nil
}

// NonBlockingSendMMsg sends multiple messages on a socket.
func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
	n, _, e := syscall.RawSyscall6(unix.SYS_SENDMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
	if e != 0 {
		return 0, TranslateErrno(e)
	}

	return int(n), nil
}

// PollEvent represents the pollfd structure passed to a poll() system call.
type PollEvent struct {
	FD      int32
	Events  int16
	Revents int16
}

// BlockingRead reads from a file descriptor that is set up as non-blocking. If
// no data is available, it will block in a poll() syscall until the file
// descriptor becomes readable.
func BlockingRead(fd int, b []byte) (int, *tcpip.Error) {
	for {
		n, _, e := syscall.RawSyscall(syscall.SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
		if e == 0 {
			return int(n), nil
		}

		event := PollEvent{
			FD:     int32(fd),
			Events: 1, // POLLIN
		}

		_, e = BlockingPoll(&event, 1, nil)
		if e != 0 && e != syscall.EINTR {
			return 0, TranslateErrno(e)
		}
	}
}

// BlockingReadv reads from a file descriptor that is set up as non-blocking and
// stores the data in a list of iovecs buffers. If no data is available, it will
// block in a poll() syscall until the file descriptor becomes readable.
func BlockingReadv(fd int, iovecs []syscall.Iovec) (int, *tcpip.Error) {
	for {
		n, _, e := syscall.RawSyscall(syscall.SYS_READV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
		if e == 0 {
			return int(n), nil
		}

		event := PollEvent{
			FD:     int32(fd),
			Events: 1, // POLLIN
		}

		_, e = BlockingPoll(&event, 1, nil)
		if e != 0 && e != syscall.EINTR {
			return 0, TranslateErrno(e)
		}
	}
}

// MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
type MMsgHdr struct {
	Msg syscall.Msghdr
	Len uint32
	_   [4]byte
}

// BlockingRecvMMsg reads from a file descriptor that is set up as non-blocking
// and stores the received messages in a slice of MMsgHdr structures. If no data
// is available, it will block in a poll() syscall until the file descriptor
// becomes readable.
func BlockingRecvMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
	for {
		n, _, e := syscall.RawSyscall6(syscall.SYS_RECVMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
		if e == 0 {
			return int(n), nil
		}

		event := PollEvent{
			FD:     int32(fd),
			Events: 1, // POLLIN
		}

		if _, e := BlockingPoll(&event, 1, nil); e != 0 && e != syscall.EINTR {
			return 0, TranslateErrno(e)
		}
	}
}
<commit_msg>Fix NonBlockingWrite3 not writing b3 if b2 is zero-length.<commit_after>// Copyright 2018 The gVisor 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.

// +build linux

// Package rawfile contains utilities for using the netstack with raw host
// files on Linux hosts.
package rawfile

import (
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
	"gvisor.dev/gvisor/pkg/tcpip"
)

// GetMTU determines the MTU of a network interface device.
func GetMTU(name string) (uint32, error) {
	fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
	if err != nil {
		return 0, err
	}

	defer syscall.Close(fd)

	var ifreq struct {
		name [16]byte
		mtu  int32
		_    [20]byte
	}

	copy(ifreq.name[:], name)
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
	if errno != 0 {
		return 0, errno
	}

	return uint32(ifreq.mtu), nil
}

// NonBlockingWrite writes the given buffer to a file descriptor. It fails if
// partial data is written.
func NonBlockingWrite(fd int, buf []byte) *tcpip.Error {
	var ptr unsafe.Pointer
	if len(buf) > 0 {
		ptr = unsafe.Pointer(&buf[0])
	}

	_, _, e := syscall.RawSyscall(syscall.SYS_WRITE, uintptr(fd), uintptr(ptr), uintptr(len(buf)))
	if e != 0 {
		return TranslateErrno(e)
	}

	return nil
}

// NonBlockingWrite3 writes up to three byte slices to a file descriptor in a
// single syscall. It fails if partial data is written.
func NonBlockingWrite3(fd int, b1, b2, b3 []byte) *tcpip.Error {
	// If there is no second and third buffer, issue a regular write.
	if len(b2) == 0 && len(b3) == 0 {
		return NonBlockingWrite(fd, b1)
	}

	// Build the iovec that represents them and issue a writev syscall.
	iovec := [3]syscall.Iovec{
		{
			Base: &b1[0],
			Len:  uint64(len(b1)),
		},
		{
			Base: &b2[0],
			Len:  uint64(len(b2)),
		},
	}
	iovecLen := uintptr(2)

	if len(b3) > 0 {
		iovecLen++
		iovec[2].Base = &b3[0]
		iovec[2].Len = uint64(len(b3))
	}

	_, _, e := syscall.RawSyscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovec[0])), iovecLen)
	if e != 0 {
		return TranslateErrno(e)
	}

	return nil
}

// NonBlockingSendMMsg sends multiple messages on a socket.
func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
	n, _, e := syscall.RawSyscall6(unix.SYS_SENDMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
	if e != 0 {
		return 0, TranslateErrno(e)
	}

	return int(n), nil
}

// PollEvent represents the pollfd structure passed to a poll() system call.
type PollEvent struct {
	FD      int32
	Events  int16
	Revents int16
}

// BlockingRead reads from a file descriptor that is set up as non-blocking. If
// no data is available, it will block in a poll() syscall until the file
// descriptor becomes readable.
func BlockingRead(fd int, b []byte) (int, *tcpip.Error) {
	for {
		n, _, e := syscall.RawSyscall(syscall.SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
		if e == 0 {
			return int(n), nil
		}

		event := PollEvent{
			FD:     int32(fd),
			Events: 1, // POLLIN
		}

		_, e = BlockingPoll(&event, 1, nil)
		if e != 0 && e != syscall.EINTR {
			return 0, TranslateErrno(e)
		}
	}
}

// BlockingReadv reads from a file descriptor that is set up as non-blocking and
// stores the data in a list of iovecs buffers. If no data is available, it will
// block in a poll() syscall until the file descriptor becomes readable.
func BlockingReadv(fd int, iovecs []syscall.Iovec) (int, *tcpip.Error) {
	for {
		n, _, e := syscall.RawSyscall(syscall.SYS_READV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
		if e == 0 {
			return int(n), nil
		}

		event := PollEvent{
			FD:     int32(fd),
			Events: 1, // POLLIN
		}

		_, e = BlockingPoll(&event, 1, nil)
		if e != 0 && e != syscall.EINTR {
			return 0, TranslateErrno(e)
		}
	}
}

// MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
type MMsgHdr struct {
	Msg syscall.Msghdr
	Len uint32
	_   [4]byte
}

// BlockingRecvMMsg reads from a file descriptor that is set up as non-blocking
// and stores the received messages in a slice of MMsgHdr structures. If no data
// is available, it will block in a poll() syscall until the file descriptor
// becomes readable.
func BlockingRecvMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
	for {
		n, _, e := syscall.RawSyscall6(syscall.SYS_RECVMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
		if e == 0 {
			return int(n), nil
		}

		event := PollEvent{
			FD:     int32(fd),
			Events: 1, // POLLIN
		}

		if _, e := BlockingPoll(&event, 1, nil); e != 0 && e != syscall.EINTR {
			return 0, TranslateErrno(e)
		}
	}
}
<|endoftext|>


<commit_before>package sockaddr_test

import (
	"fmt"
	"testing"

	"github.com/hashicorp/go-sockaddr"
)

// TODO(sean@): Either extend this test to include IPv6Addr and UnixSock, or
// remove and find a good home to test this functionality elsewhere.

func TestSockAddr_New(t *testing.T) {
	type SockAddrFixture struct {
		input              string
		ResultType         string
		NetworkAddress     string
		BroadcastAddress   string
		IPUint32           sockaddr.IPv4Address
		Maskbits           int
		BinString          string
		HexString          string
		FirstUsableAddress string
		LastUsableAddress  string
	}
	type SockAddrFixtures []SockAddrFixtures

	goodResults := []SockAddrFixture{
		{
			input:              "0.0.0.0",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.0",
			BroadcastAddress:   "0.0.0.0",
			Maskbits:           32,
			IPUint32:           0,
			BinString:          "00000000000000000000000000000000",
			HexString:          "00000000",
			FirstUsableAddress: "0.0.0.0",
			LastUsableAddress:  "0.0.0.0",
		},
		{
			input:              "0.0.0.0/0",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.0",
			BroadcastAddress:   "255.255.255.255",
			Maskbits:           0,
			IPUint32:           0,
			BinString:          "00000000000000000000000000000000",
			HexString:          "00000000",
			FirstUsableAddress: "0.0.0.1",
			LastUsableAddress:  "255.255.255.254",
		},
		{
			input:              "0.0.0.1",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.1",
			BroadcastAddress:   "0.0.0.1",
			Maskbits:           32,
			IPUint32:           1,
			BinString:          "00000000000000000000000000000001",
			HexString:          "00000001",
			FirstUsableAddress: "0.0.0.1",
			LastUsableAddress:  "0.0.0.1",
		},
		{
			input:              "0.0.0.1/1",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.0",
			BroadcastAddress:   "127.255.255.255",
			Maskbits:           1,
			IPUint32:           1,
			BinString:          "00000000000000000000000000000001",
			HexString:          "00000001",
			FirstUsableAddress: "0.0.0.1",
			LastUsableAddress:  "127.255.255.254",
		},
		{
			input:              "128.0.0.0",
			ResultType:         "ipv4",
			NetworkAddress:     "128.0.0.0",
			BroadcastAddress:   "128.0.0.0",
			Maskbits:           32,
			IPUint32:           2147483648,
			BinString:          "10000000000000000000000000000000",
			HexString:          "80000000",
			FirstUsableAddress: "128.0.0.0",
			LastUsableAddress:  "128.0.0.0",
		},
		{
			input:              "255.255.255.255",
			ResultType:         "ipv4",
			NetworkAddress:     "255.255.255.255",
			BroadcastAddress:   "255.255.255.255",
			Maskbits:           32,
			IPUint32:           4294967295,
			BinString:          "11111111111111111111111111111111",
			HexString:          "ffffffff",
			FirstUsableAddress: "255.255.255.255",
			LastUsableAddress:  "255.255.255.255",
		},
		{
			input:              "1.2.3.4",
			ResultType:         "ipv4",
			NetworkAddress:     "1.2.3.4",
			BroadcastAddress:   "1.2.3.4",
			Maskbits:           32,
			IPUint32:           16909060,
			BinString:          "00000001000000100000001100000100",
			HexString:          "01020304",
			FirstUsableAddress: "1.2.3.4",
			LastUsableAddress:  "1.2.3.4",
		},
		{
			input:              "192.168.10.10/16",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.0.0",
			BroadcastAddress:   "192.168.255.255",
			Maskbits:           16,
			IPUint32:           3232238090,
			BinString:          "11000000101010000000101000001010",
			HexString:          "c0a80a0a",
			FirstUsableAddress: "192.168.0.1",
			LastUsableAddress:  "192.168.255.254",
		},
		{
			input:              "192.168.1.10/24",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.1.0",
			BroadcastAddress:   "192.168.1.255",
			Maskbits:           24,
			IPUint32:           3232235786,
			BinString:          "11000000101010000000000100001010",
			HexString:          "c0a8010a",
			FirstUsableAddress: "192.168.1.1",
			LastUsableAddress:  "192.168.1.254",
		},
		{
			input:              "192.168.0.1",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.0.1",
			BroadcastAddress:   "192.168.0.1",
			Maskbits:           32,
			IPUint32:           3232235521,
			BinString:          "11000000101010000000000000000001",
			HexString:          "c0a80001",
			FirstUsableAddress: "192.168.0.1",
			LastUsableAddress:  "192.168.0.1",
		},
		{
			input:              "192.168.0.2/31",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.0.2",
			BroadcastAddress:   "192.168.0.3",
			Maskbits:           31,
			IPUint32:           3232235522,
			BinString:          "11000000101010000000000000000010",
			HexString:          "c0a80002",
			FirstUsableAddress: "192.168.0.2",
			LastUsableAddress:  "192.168.0.3",
		},
		{
			input:              "240.0.0.0/4",
			ResultType:         "ipv4",
			NetworkAddress:     "240.0.0.0",
			BroadcastAddress:   "255.255.255.255",
			Maskbits:           4,
			IPUint32:           4026531840,
			BinString:          "11110000000000000000000000000000",
			HexString:          "f0000000",
			FirstUsableAddress: "240.0.0.1",
			LastUsableAddress:  "255.255.255.254",
		},
	}

	for idx, r := range goodResults {
		t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
			var (
				addr sockaddr.IPAddr
				str  string
			)

			sa, err := sockaddr.NewSockAddr(r.input)
			if err != nil {
				t.Fatalf("Failed parse %s", r.input)
			}

			switch r.ResultType {
			case "ipv4":
				ipv4b, err := sockaddr.NewIPv4Addr(r.input)
				if err != nil {
					t.Fatalf("[%d] Unable to construct a new IPv4 from %s: %s", idx, r.input, err)
				}
				if !ipv4b.Equal(sa) {
					t.Fatalf("[%d] Equality comparison failed on fresh IPv4", idx)
				}

				type_ := sa.Type()
				if type_ != sockaddr.TypeIPv4 {
					t.Fatalf("[%d] Type mismatch for %s: %d", idx, r.input, type_)
				}

				ipv4 := sockaddr.ToIPv4Addr(sa)
				if ipv4 == nil {
					t.Fatalf("[%d] Failed ToIPv4Addr() %s", idx, r.input)
				}

				addr = ipv4.Broadcast()
				if addr == nil || addr.NetIP().To4().String() != r.BroadcastAddress {
					t.Fatalf("Failed IPv4Addr.BroadcastAddress() %s: expected %+q, received %+q", r.input, r.BroadcastAddress, addr.NetIP().To4().String())
				}

				maskbits := ipv4.Maskbits()
				if maskbits != r.Maskbits {
					t.Fatalf("Failed Maskbits %s: %d != %d", r.input, maskbits, r.Maskbits)
				}

				if ipv4.Address != r.IPUint32 {
					t.Fatalf("Failed ToUint32() %s: %d != %d", r.input, ipv4.Address, r.IPUint32)
				}

				str = ipv4.AddressBinString()
				if str != r.BinString {
					t.Fatalf("Failed BinString %s: %s != %s", r.input, str, r.BinString)
				}

				str = ipv4.AddressHexString()
				if str != r.HexString {
					t.Fatalf("Failed HexString %s: %s != %s", r.input, str, r.HexString)
				}

				addr = ipv4.Network()
				if addr == nil || addr.NetIP().To4().String() != r.NetworkAddress {
					t.Fatalf("Failed NetworkAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.NetworkAddress)
				}

				addr = ipv4.FirstUsable()
				if addr == nil || addr.NetIP().To4().String() != r.FirstUsableAddress {
					t.Fatalf("Failed FirstUsableAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.FirstUsableAddress)
				}

				addr = ipv4.LastUsable()
				if addr == nil || addr.NetIP().To4().String() != r.LastUsableAddress {
					t.Fatalf("Failed LastUsableAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.LastUsableAddress)
				}
			default:
				t.Fatalf("Unknown result type: %s", r.ResultType)
			}
		})
	}

	badResults := []string{
		"256.0.0.0",
		"0.0.0.0.0",
	}

	for idx, badIP := range badResults {
		t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
			sa, err := sockaddr.NewSockAddr(badIP)
			if err == nil {
				t.Fatalf("Failed should have failed to parse %s: %v", badIP, sa)
			}
			if sa != nil {
				t.Fatalf("SockAddr should be nil")
			}
		})
	}

}
<commit_msg>Increase the test coverage of SockAddr to its limit.<commit_after>package sockaddr_test

import (
	"fmt"
	"testing"

	"github.com/hashicorp/go-sockaddr"
)

// TODO(sean@): Either extend this test to include IPv6Addr and UnixSock, or
// remove and find a good home to test this functionality elsewhere.

func TestSockAddr_New(t *testing.T) {
	type SockAddrFixture struct {
		input              string
		ResultType         string
		NetworkAddress     string
		BroadcastAddress   string
		IPUint32           sockaddr.IPv4Address
		Maskbits           int
		BinString          string
		HexString          string
		FirstUsableAddress string
		LastUsableAddress  string
	}
	type SockAddrFixtures []SockAddrFixtures

	goodResults := []SockAddrFixture{
		{
			input:              "0.0.0.0",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.0",
			BroadcastAddress:   "0.0.0.0",
			Maskbits:           32,
			IPUint32:           0,
			BinString:          "00000000000000000000000000000000",
			HexString:          "00000000",
			FirstUsableAddress: "0.0.0.0",
			LastUsableAddress:  "0.0.0.0",
		},
		{
			input:              "0.0.0.0/0",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.0",
			BroadcastAddress:   "255.255.255.255",
			Maskbits:           0,
			IPUint32:           0,
			BinString:          "00000000000000000000000000000000",
			HexString:          "00000000",
			FirstUsableAddress: "0.0.0.1",
			LastUsableAddress:  "255.255.255.254",
		},
		{
			input:              "0.0.0.1",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.1",
			BroadcastAddress:   "0.0.0.1",
			Maskbits:           32,
			IPUint32:           1,
			BinString:          "00000000000000000000000000000001",
			HexString:          "00000001",
			FirstUsableAddress: "0.0.0.1",
			LastUsableAddress:  "0.0.0.1",
		},
		{
			input:              "0.0.0.1/1",
			ResultType:         "ipv4",
			NetworkAddress:     "0.0.0.0",
			BroadcastAddress:   "127.255.255.255",
			Maskbits:           1,
			IPUint32:           1,
			BinString:          "00000000000000000000000000000001",
			HexString:          "00000001",
			FirstUsableAddress: "0.0.0.1",
			LastUsableAddress:  "127.255.255.254",
		},
		{
			input:              "128.0.0.0",
			ResultType:         "ipv4",
			NetworkAddress:     "128.0.0.0",
			BroadcastAddress:   "128.0.0.0",
			Maskbits:           32,
			IPUint32:           2147483648,
			BinString:          "10000000000000000000000000000000",
			HexString:          "80000000",
			FirstUsableAddress: "128.0.0.0",
			LastUsableAddress:  "128.0.0.0",
		},
		{
			input:              "255.255.255.255",
			ResultType:         "ipv4",
			NetworkAddress:     "255.255.255.255",
			BroadcastAddress:   "255.255.255.255",
			Maskbits:           32,
			IPUint32:           4294967295,
			BinString:          "11111111111111111111111111111111",
			HexString:          "ffffffff",
			FirstUsableAddress: "255.255.255.255",
			LastUsableAddress:  "255.255.255.255",
		},
		{
			input:              "1.2.3.4",
			ResultType:         "ipv4",
			NetworkAddress:     "1.2.3.4",
			BroadcastAddress:   "1.2.3.4",
			Maskbits:           32,
			IPUint32:           16909060,
			BinString:          "00000001000000100000001100000100",
			HexString:          "01020304",
			FirstUsableAddress: "1.2.3.4",
			LastUsableAddress:  "1.2.3.4",
		},
		{
			input:              "192.168.10.10/16",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.0.0",
			BroadcastAddress:   "192.168.255.255",
			Maskbits:           16,
			IPUint32:           3232238090,
			BinString:          "11000000101010000000101000001010",
			HexString:          "c0a80a0a",
			FirstUsableAddress: "192.168.0.1",
			LastUsableAddress:  "192.168.255.254",
		},
		{
			input:              "192.168.1.10/24",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.1.0",
			BroadcastAddress:   "192.168.1.255",
			Maskbits:           24,
			IPUint32:           3232235786,
			BinString:          "11000000101010000000000100001010",
			HexString:          "c0a8010a",
			FirstUsableAddress: "192.168.1.1",
			LastUsableAddress:  "192.168.1.254",
		},
		{
			input:              "192.168.0.1",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.0.1",
			BroadcastAddress:   "192.168.0.1",
			Maskbits:           32,
			IPUint32:           3232235521,
			BinString:          "11000000101010000000000000000001",
			HexString:          "c0a80001",
			FirstUsableAddress: "192.168.0.1",
			LastUsableAddress:  "192.168.0.1",
		},
		{
			input:              "192.168.0.2/31",
			ResultType:         "ipv4",
			NetworkAddress:     "192.168.0.2",
			BroadcastAddress:   "192.168.0.3",
			Maskbits:           31,
			IPUint32:           3232235522,
			BinString:          "11000000101010000000000000000010",
			HexString:          "c0a80002",
			FirstUsableAddress: "192.168.0.2",
			LastUsableAddress:  "192.168.0.3",
		},
		{
			input:              "240.0.0.0/4",
			ResultType:         "ipv4",
			NetworkAddress:     "240.0.0.0",
			BroadcastAddress:   "255.255.255.255",
			Maskbits:           4,
			IPUint32:           4026531840,
			BinString:          "11110000000000000000000000000000",
			HexString:          "f0000000",
			FirstUsableAddress: "240.0.0.1",
			LastUsableAddress:  "255.255.255.254",
		},
	}

	for idx, r := range goodResults {
		t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
			var (
				addr sockaddr.IPAddr
				str  string
			)

			sa, err := sockaddr.NewSockAddr(r.input)
			if err != nil {
				t.Fatalf("Failed parse %s", r.input)
			}

			switch r.ResultType {
			case "ipv4":
				ipv4b, err := sockaddr.NewIPv4Addr(r.input)
				if err != nil {
					t.Fatalf("[%d] Unable to construct a new IPv4 from %s: %s", idx, r.input, err)
				}
				if !ipv4b.Equal(sa) {
					t.Fatalf("[%d] Equality comparison failed on fresh IPv4", idx)
				}

				type_ := sa.Type()
				if type_ != sockaddr.TypeIPv4 {
					t.Fatalf("[%d] Type mismatch for %s: %d", idx, r.input, type_)
				}

				ipv4 := sockaddr.ToIPv4Addr(sa)
				if ipv4 == nil {
					t.Fatalf("[%d] Failed ToIPv4Addr() %s", idx, r.input)
				}

				addr = ipv4.Broadcast()
				if addr == nil || addr.NetIP().To4().String() != r.BroadcastAddress {
					t.Fatalf("Failed IPv4Addr.BroadcastAddress() %s: expected %+q, received %+q", r.input, r.BroadcastAddress, addr.NetIP().To4().String())
				}

				maskbits := ipv4.Maskbits()
				if maskbits != r.Maskbits {
					t.Fatalf("Failed Maskbits %s: %d != %d", r.input, maskbits, r.Maskbits)
				}

				if ipv4.Address != r.IPUint32 {
					t.Fatalf("Failed ToUint32() %s: %d != %d", r.input, ipv4.Address, r.IPUint32)
				}

				str = ipv4.AddressBinString()
				if str != r.BinString {
					t.Fatalf("Failed BinString %s: %s != %s", r.input, str, r.BinString)
				}

				str = ipv4.AddressHexString()
				if str != r.HexString {
					t.Fatalf("Failed HexString %s: %s != %s", r.input, str, r.HexString)
				}

				addr = ipv4.Network()
				if addr == nil || addr.NetIP().To4().String() != r.NetworkAddress {
					t.Fatalf("Failed NetworkAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.NetworkAddress)
				}

				addr = ipv4.FirstUsable()
				if addr == nil || addr.NetIP().To4().String() != r.FirstUsableAddress {
					t.Fatalf("Failed FirstUsableAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.FirstUsableAddress)
				}

				addr = ipv4.LastUsable()
				if addr == nil || addr.NetIP().To4().String() != r.LastUsableAddress {
					t.Fatalf("Failed LastUsableAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.LastUsableAddress)
				}
			default:
				t.Fatalf("Unknown result type: %s", r.ResultType)
			}
		})
	}

	badResults := []string{
		"256.0.0.0",
		"0.0.0.0.0",
	}

	for idx, badIP := range badResults {
		t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
			sa, err := sockaddr.NewSockAddr(badIP)
			if err == nil {
				t.Fatalf("Failed should have failed to parse %s: %v", badIP, sa)
			}
			if sa != nil {
				t.Fatalf("SockAddr should be nil")
			}
		})
	}

}

func TestSockAddrAttrs(t *testing.T) {
	const expectedNumAttrs = 2
	saa := sockaddr.SockAddrAttrs()
	if len(saa) != expectedNumAttrs {
		t.Fatalf("wrong number of SockAddrAttrs: %d vs %d", len(saa), expectedNumAttrs)
	}

	tests := []struct {
		name string
		sa   sockaddr.SockAddr
		attr sockaddr.AttrName
		want string
	}{
		{
			name: "type",
			sa:   sockaddr.MustIPv4Addr("1.2.3.4"),
			attr: "type",
			want: "IPv4",
		},
		{
			name: "string",
			sa:   sockaddr.MustIPv4Addr("1.2.3.4"),
			attr: "string",
			want: "1.2.3.4",
		},
		{
			name: "invalid",
			sa:   sockaddr.MustIPv4Addr("1.2.3.4"),
			attr: "ENOENT",
			want: "",
		},
	}

	for i, test := range tests {
		if test.name == "" {
			t.Fatalf("test %d needs a name", i)
		}

		result := sockaddr.SockAddrAttr(test.sa, test.attr)
		if result != test.want {
			t.Fatalf("%s: expected %s got %s", test.name, test.want, result)
		}
	}
}

func TestToFoo(t *testing.T) {
	tests := []struct {
		name     string
		sa       sockaddr.SockAddr
		passIP   bool
		passIPv4 bool
		passIPv6 bool
		passUnix bool
	}{
		{
			name:     "ipv4",
			sa:       sockaddr.MustIPv4Addr("1.2.3.4"),
			passIP:   true,
			passIPv4: true,
		},
		{
			name:     "ipv6",
			sa:       sockaddr.MustIPv6Addr("::1"),
			passIP:   true,
			passIPv6: true,
		},
		{
			name:     "unix",
			sa:       sockaddr.MustUnixSock("/tmp/foo"),
			passUnix: true,
		},
	}

	for i, test := range tests {
		if test.name == "" {
			t.Fatalf("test %d must have a name", i)
		}

		switch us := sockaddr.ToUnixSock(test.sa); {
		case us == nil && test.passUnix,
			us != nil && !test.passUnix:
			t.Fatalf("bad")
		}

		switch ip := sockaddr.ToIPAddr(test.sa); {
		case ip == nil && test.passIP,
			ip != nil && !test.passIP:
			t.Fatalf("bad")
		}

		switch ipv4 := sockaddr.ToIPv4Addr(test.sa); {
		case ipv4 == nil && test.passIPv4,
			ipv4 != nil && !test.passIPv4:
			t.Fatalf("bad")
		}

		switch ipv6 := sockaddr.ToIPv6Addr(test.sa); {
		case ipv6 == nil && test.passIPv6,
			ipv6 != nil && !test.passIPv6:
			t.Fatalf("bad")
		}
	}

}
<|endoftext|>


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

package multiwatcher

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"gopkg.in/juju/charm.v5"

	"github.com/juju/juju/constraints"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
)

// Life describes the lifecycle state of an entity ("alive", "dying"
// or "dead").
type Life string

// Status represents the status of an entity.
// It could be a service, unit, machine or its agent.
type Status string

// EntityInfo is implemented by all entity Info types.
type EntityInfo interface {
	// EntityId returns an identifier that will uniquely
	// identify the entity within its kind
	EntityId() EntityId
}

// EntityId unique identifies an entity being tracked by the
// multiwatcherStore.
type EntityId struct {
	Kind    string
	EnvUUID string
	Id      string
}

// Delta holds details of a change to the environment.
type Delta struct {
	// If Removed is true, the entity has been removed;
	// otherwise it has been created or changed.
	Removed bool
	// Entity holds data about the entity that has changed.
	Entity EntityInfo
}

// MarshalJSON implements json.Marshaler.
func (d *Delta) MarshalJSON() ([]byte, error) {
	b, err := json.Marshal(d.Entity)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	buf.WriteByte('[')
	c := "change"
	if d.Removed {
		c = "remove"
	}
	fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c)
	buf.Write(b)
	buf.WriteByte(']')
	return buf.Bytes(), nil
}

// UnmarshalJSON implements json.Unmarshaler.
func (d *Delta) UnmarshalJSON(data []byte) error {
	var elements []json.RawMessage
	if err := json.Unmarshal(data, &elements); err != nil {
		return err
	}
	if len(elements) != 3 {
		return fmt.Errorf(
			"Expected 3 elements in top-level of JSON but got %d",
			len(elements))
	}
	var entityKind, operation string
	if err := json.Unmarshal(elements[0], &entityKind); err != nil {
		return err
	}
	if err := json.Unmarshal(elements[1], &operation); err != nil {
		return err
	}
	if operation == "remove" {
		d.Removed = true
	} else if operation != "change" {
		return fmt.Errorf("Unexpected operation %q", operation)
	}
	switch entityKind {
	case "environment":
		d.Entity = new(EnvironmentInfo)
	case "machine":
		d.Entity = new(MachineInfo)
	case "service":
		d.Entity = new(ServiceInfo)
	case "unit":
		d.Entity = new(UnitInfo)
	case "relation":
		d.Entity = new(RelationInfo)
	case "annotation":
		d.Entity = new(AnnotationInfo)
	case "block":
		d.Entity = new(BlockInfo)
	case "action":
		d.Entity = new(ActionInfo)
	default:
		return fmt.Errorf("Unexpected entity name %q", entityKind)
	}
	return json.Unmarshal(elements[2], &d.Entity)
}

// When remote units leave scope, their ids will be noted in the
// Departed field, and no further events will be sent for those units.
type RelationUnitsChange struct {
	Changed  map[string]UnitSettings
	Departed []string
}

// UnitSettings holds information about a service unit's settings
// within a relation.
type UnitSettings struct {
	Version int64
}

// MachineInfo holds the information about a machine
// that is tracked by multiwatcherStore.
type MachineInfo struct {
	EnvUUID                  string
	Id                       string
	InstanceId               string
	Status                   Status
	StatusInfo               string
	StatusData               map[string]interface{}
	Life                     Life
	Series                   string
	SupportedContainers      []instance.ContainerType
	SupportedContainersKnown bool
	HardwareCharacteristics  *instance.HardwareCharacteristics `json:",omitempty"`
	Jobs                     []MachineJob
	Addresses                []network.Address
	HasVote                  bool
	WantsVote                bool
}

// EntityId returns a unique identifier for a machine across
// environments.
func (i *MachineInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "machine",
		EnvUUID: i.EnvUUID,
		Id:      i.Id,
	}
}

// StatusInfo holds the unit and machine status information. It is
// used by ServiceInfo and UnitInfo.
type StatusInfo struct {
	Err     error
	Current Status
	Message string
	Since   *time.Time
	Version string
	Data    map[string]interface{}
}

// ServiceInfo holds the information about a service that is tracked
// by multiwatcherStore.
type ServiceInfo struct {
	EnvUUID     string
	Name        string
	Exposed     bool
	CharmURL    string
	OwnerTag    string
	Life        Life
	MinUnits    int
	Constraints constraints.Value
	Config      map[string]interface{}
	Subordinate bool
	Status      StatusInfo
}

// EntityId returns a unique identifier for a service across
// environments.
func (i *ServiceInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "service",
		EnvUUID: i.EnvUUID,
		Id:      i.Name,
	}
}

// UnitInfo holds the information about a unit
// that is tracked by multiwatcherStore.
type UnitInfo struct {
	EnvUUID        string
	Name           string
	Service        string
	Series         string
	CharmURL       string
	PublicAddress  string
	PrivateAddress string
	MachineId      string
	Ports          []network.Port
	PortRanges     []network.PortRange
	Subordinate    bool
	// The following 3 status values are deprecated.
	Status     Status
	StatusInfo string
	StatusData map[string]interface{}
	// Workload and agent state are modelled separately.
	WorkloadStatus StatusInfo
	AgentStatus    StatusInfo
}

// EntityId returns a unique identifier for a unit across
// environments.
func (i *UnitInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "unit",
		EnvUUID: i.EnvUUID,
		Id:      i.Name,
	}
}

// ActionInfo holds the information about a action that is tracked by
// multiwatcherStore.
type ActionInfo struct {
	EnvUUID    string
	Id         string
	Receiver   string
	Name       string
	Parameters map[string]interface{}
	Status     string
	Message    string
	Results    map[string]interface{}
	Enqueued   time.Time
	Started    time.Time
	Completed  time.Time
}

// EntityId returns a unique identifier for an action across
// environments.
func (i *ActionInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "action",
		EnvUUID: i.EnvUUID,
		Id:      i.Id,
	}
}

// RelationInfo holds the information about a relation that is tracked
// by multiwatcherStore.
type RelationInfo struct {
	EnvUUID   string
	Key       string
	Id        int
	Endpoints []Endpoint
}

// Endpoint holds a service-relation pair.
type Endpoint struct {
	ServiceName string
	Relation    charm.Relation
}

// EntityId returns a unique identifier for a relation across
// environments.
func (i *RelationInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "relation",
		EnvUUID: i.EnvUUID,
		Id:      i.Key,
	}
}

// AnnotationInfo holds the information about an annotation that is
// tracked by multiwatcherStore.
type AnnotationInfo struct {
	EnvUUID     string
	Tag         string
	Annotations map[string]string
}

// EntityId returns a unique identifier for an annotation across
// environments.
func (i *AnnotationInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "annotation",
		EnvUUID: i.EnvUUID,
		Id:      i.Tag,
	}
}

// MachineJob values define responsibilities that machines may be
// expected to fulfil.
type MachineJob string

const (
	JobHostUnits        MachineJob = "JobHostUnits"
	JobManageEnviron    MachineJob = "JobManageEnviron"
	JobManageNetworking MachineJob = "JobManageNetworking"

	// Deprecated in 1.18
	JobManageStateDeprecated MachineJob = "JobManageState"
)

// NeedsState returns true if the job requires a state connection.
func (job MachineJob) NeedsState() bool {
	return job == JobManageEnviron
}

// AnyJobNeedsState returns true if any of the provided jobs
// require a state connection.
func AnyJobNeedsState(jobs ...MachineJob) bool {
	for _, j := range jobs {
		if j.NeedsState() {
			return true
		}
	}
	return false
}

// BlockInfo holds the information about a block that is tracked by
// multiwatcherStore.
type BlockInfo struct {
	EnvUUID string
	Id      string
	Type    BlockType
	Message string
	Tag     string
}

// EntityId returns a unique identifier for a block across
// environments.
func (i *BlockInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "block",
		EnvUUID: i.EnvUUID,
		Id:      i.Id,
	}
}

// BlockType values define environment block type.
type BlockType string

const (
	// BlockDestroy type identifies destroy blocks.
	BlockDestroy BlockType = "BlockDestroy"

	// BlockRemove type identifies remove blocks.
	BlockRemove BlockType = "BlockRemove"

	// BlockChange type identifies change blocks.
	BlockChange BlockType = "BlockChange"
)

// EnvironmentInfo holds the information about an environment that is
// tracked by multiwatcherStore.
type EnvironmentInfo struct {
	EnvUUID    string
	Name       string
	Life       Life
	Owner      string
	ServerUUID string
}

// EntityId returns a unique identifier for an environment.
func (i *EnvironmentInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "environment",
		EnvUUID: i.EnvUUID,
		Id:      i.EnvUUID,
	}
}
<commit_msg>state/multiwatcher: fixed typo in doc comment<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package multiwatcher

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"gopkg.in/juju/charm.v5"

	"github.com/juju/juju/constraints"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
)

// Life describes the lifecycle state of an entity ("alive", "dying"
// or "dead").
type Life string

// Status represents the status of an entity.
// It could be a service, unit, machine or its agent.
type Status string

// EntityInfo is implemented by all entity Info types.
type EntityInfo interface {
	// EntityId returns an identifier that will uniquely
	// identify the entity within its kind
	EntityId() EntityId
}

// EntityId uniquely identifies an entity being tracked by the
// multiwatcherStore.
type EntityId struct {
	Kind    string
	EnvUUID string
	Id      string
}

// Delta holds details of a change to the environment.
type Delta struct {
	// If Removed is true, the entity has been removed;
	// otherwise it has been created or changed.
	Removed bool
	// Entity holds data about the entity that has changed.
	Entity EntityInfo
}

// MarshalJSON implements json.Marshaler.
func (d *Delta) MarshalJSON() ([]byte, error) {
	b, err := json.Marshal(d.Entity)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	buf.WriteByte('[')
	c := "change"
	if d.Removed {
		c = "remove"
	}
	fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c)
	buf.Write(b)
	buf.WriteByte(']')
	return buf.Bytes(), nil
}

// UnmarshalJSON implements json.Unmarshaler.
func (d *Delta) UnmarshalJSON(data []byte) error {
	var elements []json.RawMessage
	if err := json.Unmarshal(data, &elements); err != nil {
		return err
	}
	if len(elements) != 3 {
		return fmt.Errorf(
			"Expected 3 elements in top-level of JSON but got %d",
			len(elements))
	}
	var entityKind, operation string
	if err := json.Unmarshal(elements[0], &entityKind); err != nil {
		return err
	}
	if err := json.Unmarshal(elements[1], &operation); err != nil {
		return err
	}
	if operation == "remove" {
		d.Removed = true
	} else if operation != "change" {
		return fmt.Errorf("Unexpected operation %q", operation)
	}
	switch entityKind {
	case "environment":
		d.Entity = new(EnvironmentInfo)
	case "machine":
		d.Entity = new(MachineInfo)
	case "service":
		d.Entity = new(ServiceInfo)
	case "unit":
		d.Entity = new(UnitInfo)
	case "relation":
		d.Entity = new(RelationInfo)
	case "annotation":
		d.Entity = new(AnnotationInfo)
	case "block":
		d.Entity = new(BlockInfo)
	case "action":
		d.Entity = new(ActionInfo)
	default:
		return fmt.Errorf("Unexpected entity name %q", entityKind)
	}
	return json.Unmarshal(elements[2], &d.Entity)
}

// When remote units leave scope, their ids will be noted in the
// Departed field, and no further events will be sent for those units.
type RelationUnitsChange struct {
	Changed  map[string]UnitSettings
	Departed []string
}

// UnitSettings holds information about a service unit's settings
// within a relation.
type UnitSettings struct {
	Version int64
}

// MachineInfo holds the information about a machine
// that is tracked by multiwatcherStore.
type MachineInfo struct {
	EnvUUID                  string
	Id                       string
	InstanceId               string
	Status                   Status
	StatusInfo               string
	StatusData               map[string]interface{}
	Life                     Life
	Series                   string
	SupportedContainers      []instance.ContainerType
	SupportedContainersKnown bool
	HardwareCharacteristics  *instance.HardwareCharacteristics `json:",omitempty"`
	Jobs                     []MachineJob
	Addresses                []network.Address
	HasVote                  bool
	WantsVote                bool
}

// EntityId returns a unique identifier for a machine across
// environments.
func (i *MachineInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "machine",
		EnvUUID: i.EnvUUID,
		Id:      i.Id,
	}
}

// StatusInfo holds the unit and machine status information. It is
// used by ServiceInfo and UnitInfo.
type StatusInfo struct {
	Err     error
	Current Status
	Message string
	Since   *time.Time
	Version string
	Data    map[string]interface{}
}

// ServiceInfo holds the information about a service that is tracked
// by multiwatcherStore.
type ServiceInfo struct {
	EnvUUID     string
	Name        string
	Exposed     bool
	CharmURL    string
	OwnerTag    string
	Life        Life
	MinUnits    int
	Constraints constraints.Value
	Config      map[string]interface{}
	Subordinate bool
	Status      StatusInfo
}

// EntityId returns a unique identifier for a service across
// environments.
func (i *ServiceInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "service",
		EnvUUID: i.EnvUUID,
		Id:      i.Name,
	}
}

// UnitInfo holds the information about a unit
// that is tracked by multiwatcherStore.
type UnitInfo struct {
	EnvUUID        string
	Name           string
	Service        string
	Series         string
	CharmURL       string
	PublicAddress  string
	PrivateAddress string
	MachineId      string
	Ports          []network.Port
	PortRanges     []network.PortRange
	Subordinate    bool
	// The following 3 status values are deprecated.
	Status     Status
	StatusInfo string
	StatusData map[string]interface{}
	// Workload and agent state are modelled separately.
	WorkloadStatus StatusInfo
	AgentStatus    StatusInfo
}

// EntityId returns a unique identifier for a unit across
// environments.
func (i *UnitInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "unit",
		EnvUUID: i.EnvUUID,
		Id:      i.Name,
	}
}

// ActionInfo holds the information about a action that is tracked by
// multiwatcherStore.
type ActionInfo struct {
	EnvUUID    string
	Id         string
	Receiver   string
	Name       string
	Parameters map[string]interface{}
	Status     string
	Message    string
	Results    map[string]interface{}
	Enqueued   time.Time
	Started    time.Time
	Completed  time.Time
}

// EntityId returns a unique identifier for an action across
// environments.
func (i *ActionInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "action",
		EnvUUID: i.EnvUUID,
		Id:      i.Id,
	}
}

// RelationInfo holds the information about a relation that is tracked
// by multiwatcherStore.
type RelationInfo struct {
	EnvUUID   string
	Key       string
	Id        int
	Endpoints []Endpoint
}

// Endpoint holds a service-relation pair.
type Endpoint struct {
	ServiceName string
	Relation    charm.Relation
}

// EntityId returns a unique identifier for a relation across
// environments.
func (i *RelationInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "relation",
		EnvUUID: i.EnvUUID,
		Id:      i.Key,
	}
}

// AnnotationInfo holds the information about an annotation that is
// tracked by multiwatcherStore.
type AnnotationInfo struct {
	EnvUUID     string
	Tag         string
	Annotations map[string]string
}

// EntityId returns a unique identifier for an annotation across
// environments.
func (i *AnnotationInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "annotation",
		EnvUUID: i.EnvUUID,
		Id:      i.Tag,
	}
}

// MachineJob values define responsibilities that machines may be
// expected to fulfil.
type MachineJob string

const (
	JobHostUnits        MachineJob = "JobHostUnits"
	JobManageEnviron    MachineJob = "JobManageEnviron"
	JobManageNetworking MachineJob = "JobManageNetworking"

	// Deprecated in 1.18
	JobManageStateDeprecated MachineJob = "JobManageState"
)

// NeedsState returns true if the job requires a state connection.
func (job MachineJob) NeedsState() bool {
	return job == JobManageEnviron
}

// AnyJobNeedsState returns true if any of the provided jobs
// require a state connection.
func AnyJobNeedsState(jobs ...MachineJob) bool {
	for _, j := range jobs {
		if j.NeedsState() {
			return true
		}
	}
	return false
}

// BlockInfo holds the information about a block that is tracked by
// multiwatcherStore.
type BlockInfo struct {
	EnvUUID string
	Id      string
	Type    BlockType
	Message string
	Tag     string
}

// EntityId returns a unique identifier for a block across
// environments.
func (i *BlockInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "block",
		EnvUUID: i.EnvUUID,
		Id:      i.Id,
	}
}

// BlockType values define environment block type.
type BlockType string

const (
	// BlockDestroy type identifies destroy blocks.
	BlockDestroy BlockType = "BlockDestroy"

	// BlockRemove type identifies remove blocks.
	BlockRemove BlockType = "BlockRemove"

	// BlockChange type identifies change blocks.
	BlockChange BlockType = "BlockChange"
)

// EnvironmentInfo holds the information about an environment that is
// tracked by multiwatcherStore.
type EnvironmentInfo struct {
	EnvUUID    string
	Name       string
	Life       Life
	Owner      string
	ServerUUID string
}

// EntityId returns a unique identifier for an environment.
func (i *EnvironmentInfo) EntityId() EntityId {
	return EntityId{
		Kind:    "environment",
		EnvUUID: i.EnvUUID,
		Id:      i.EnvUUID,
	}
}
<|endoftext|>


<commit_before>package crackByteXOR

import (
	"encoding/hex"
	"testing"

	"gonum.org/v1/gonum/stat/distuv"

	"github.com/eseymour/cryptopals/pkg/crypto/analysis"
	"github.com/eseymour/cryptopals/pkg/util/bytes"
)

func TestCrackByteXOR(t *testing.T) {
	ciphertext := bytes.Must(hex.DecodeString("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"))
	confidence := 0.99

	_, chiSquare := analysis.BreakXOREncryptByteKey(ciphertext)

	chiSquaredDist := distuv.ChiSquared{25, nil}

	p := chiSquaredDist.CDF(chiSquare)
	if p > confidence {
		t.Errorf("BreakXOREncryptByteKey(%x) key is likely not correct with p-value %f", ciphertext, p)
	}
}
<commit_msg>Update challenge 3 test<commit_after>package crackByteXOR

import (
	"encoding/hex"
	"testing"

	"github.com/eseymour/cryptopals/pkg/crypto/analysis"
	"github.com/eseymour/cryptopals/pkg/util/bytes"
)

func TestCrackByteXOR(t *testing.T) {
	ciphertext := bytes.Must(hex.DecodeString("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"))
	threshold := 0.10

	_, pValue := analysis.BreakXOREncryptByteKey(ciphertext)

	if pValue < threshold {
		t.Errorf("BreakXOREncryptByteKey(%x) key is likely not correct with p-value %f", ciphertext, pValue)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Smudge 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 smudge

import (
	"os"
	"regexp"
	"strconv"
	"strings"
)

// Provides a series of methods and constants that revolve around the getting
// (or programmatically setting/overriding) environmental properties, returning
// default values if not set.

const (
	// EnvVarHeartbeatMillis is the name of the environment variable that
	// sets the heartbeat frequency (in millis).
	EnvVarHeartbeatMillis = "SMUDGE_HEARTBEAT_MILLIS"

	// DefaultHeartbeatMillis is the default heartbeat frequency (in millis).
	DefaultHeartbeatMillis int = 250

	// EnvVarInitialHosts is the name of the environment variable that sets
	// the initial known hosts. The value it sets should be a comma-delimitted
	// string of one or more IP:PORT pairs (port is optional if it matched the
	// value of SMUDGE_LISTEN_PORT).
	EnvVarInitialHosts = "SMUDGE_INITIAL_HOSTS"

	// DefaultInitialHosts default lists of initially known hosts.
	DefaultInitialHosts string = ""

	// EnvVarListenPort is the name of the environment variable that sets
	// the UDP listen port.
	EnvVarListenPort = "SMUDGE_LISTEN_PORT"

	// DefaultListenPort is the default UDP listen port.
	DefaultListenPort int = 9999
)

var heartbeatMillis int

var listenPort int

var initialHosts []string

const stringListDelimitRegex = "\\s*,?\\s+"

// GetInitialHosts returns the list of initially known hosts.
func GetInitialHosts() []string {
	if initialHosts == nil {
		initialHosts = getStringArrayVar(EnvVarInitialHosts, DefaultInitialHosts)
	}

	return initialHosts
}

// GetListenPort returns the port that this host will listen on.
func GetListenPort() int {
	if listenPort == 0 {
		listenPort = getIntVar(EnvVarListenPort, DefaultListenPort)
	}

	return listenPort
}

// SetListenPort sets the UDP port to listen on. It has no effect once
// Begin() has been called.
func SetListenPort(p int) {
	if p == 0 {
		listenPort = DefaultListenPort
	} else {
		listenPort = p
	}
}

// GetHeartbeatMillis gets this host's heartbeat frequency in milliseconds.
func GetHeartbeatMillis() int {
	if heartbeatMillis == 0 {
		heartbeatMillis = getIntVar(EnvVarHeartbeatMillis, DefaultHeartbeatMillis)
	}

	return heartbeatMillis
}

// SetHeartbeatMillis sets this nodes heartbeat frequency. Unlike
// SetListenPort(), calling this function after Begin() has been called will
// have an effect.
func SetHeartbeatMillis(val int) {
	heartbeatMillis = val
}

// Gets an environmental variable "key". If it does not exist, "defaultVal" is
// returned; if it does, it attempts to convert to an integer, returning
// "defaultVal" is it fails.
func getIntVar(key string, defaultVal int) int {
	valueString := os.Getenv(key)
	valueInt := defaultVal

	if valueString != "" {
		i, err := strconv.Atoi(key)

		if err != nil {
			logfWarn("Failed to parse env property %s: %s is not "+
				"an integer. Using default.\n", key, valueString)
		} else {
			valueInt = i
		}
	}

	return valueInt
}

// Gets an environmental variable "key". If it does not exist, "defaultVal" is
// returned; if it does, it attempts to convert to a string slice, returning
// "defaultVal" is it fails.
func getStringArrayVar(key string, defaultVal string) []string {
	valueString := os.Getenv(key)

	if valueString == "" {
		valueString = defaultVal
	}

	valueSlice := splitDelimmitedString(valueString, stringListDelimitRegex)

	return valueSlice
}

// Splits a string on a regular expression.
func splitDelimmitedString(str string, regex string) []string {
	var result []string

	str = strings.TrimSpace(str)

	if str != "" {
		reg := regexp.MustCompile(regex)
		indices := reg.FindAllStringIndex(str, -1)

		result = make([]string, len(indices)+1)

		lastStart := 0
		for i, val := range indices {
			result[i] = str[lastStart:val[0]]
			lastStart = val[1]
		}

		result[len(indices)] = str[lastStart:len(str)]

		// Special case of single empty string
		if len(result) == 1 && result[0] == "" {
			result = make([]string, 0, 0)
		}
	}

	return result
}
<commit_msg>srsly?<commit_after>/*
Copyright 2016 The Smudge 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 smudge

import (
	"os"
	"regexp"
	"strconv"
	"strings"
)

// Provides a series of methods and constants that revolve around the getting
// (or programmatically setting/overriding) environmental properties, returning
// default values if not set.

const (
	// EnvVarHeartbeatMillis is the name of the environment variable that
	// sets the heartbeat frequency (in millis).
	EnvVarHeartbeatMillis = "SMUDGE_HEARTBEAT_MILLIS"

	// DefaultHeartbeatMillis is the default heartbeat frequency (in millis).
	DefaultHeartbeatMillis int = 250

	// EnvVarInitialHosts is the name of the environment variable that sets
	// the initial known hosts. The value it sets should be a comma-delimitted
	// string of one or more IP:PORT pairs (port is optional if it matched the
	// value of SMUDGE_LISTEN_PORT).
	EnvVarInitialHosts = "SMUDGE_INITIAL_HOSTS"

	// DefaultInitialHosts default lists of initially known hosts.
	DefaultInitialHosts string = ""

	// EnvVarListenPort is the name of the environment variable that sets
	// the UDP listen port.
	EnvVarListenPort = "SMUDGE_LISTEN_PORT"

	// DefaultListenPort is the default UDP listen port.
	DefaultListenPort int = 9999
)

var heartbeatMillis int

var listenPort int

var initialHosts []string

const stringListDelimitRegex = "\\s*,?\\s+"

// GetInitialHosts returns the list of initially known hosts.
func GetInitialHosts() []string {
	if initialHosts == nil {
		initialHosts = getStringArrayVar(EnvVarInitialHosts, DefaultInitialHosts)
	}

	return initialHosts
}

// GetListenPort returns the port that this host will listen on.
func GetListenPort() int {
	if listenPort == 0 {
		listenPort = getIntVar(EnvVarListenPort, DefaultListenPort)
	}

	return listenPort
}

// SetListenPort sets the UDP port to listen on. It has no effect once
// Begin() has been called.
func SetListenPort(p int) {
	if p == 0 {
		listenPort = DefaultListenPort
	} else {
		listenPort = p
	}
}

// GetHeartbeatMillis gets this host's heartbeat frequency in milliseconds.
func GetHeartbeatMillis() int {
	if heartbeatMillis == 0 {
		heartbeatMillis = getIntVar(EnvVarHeartbeatMillis, DefaultHeartbeatMillis)
	}

	return heartbeatMillis
}

// SetHeartbeatMillis sets this nodes heartbeat frequency. Unlike
// SetListenPort(), calling this function after Begin() has been called will
// have an effect.
func SetHeartbeatMillis(val int) {
	heartbeatMillis = val
}

// Gets an environmental variable "key". If it does not exist, "defaultVal" is
// returned; if it does, it attempts to convert to an integer, returning
// "defaultVal" is it fails.
func getIntVar(key string, defaultVal int) int {
	valueString := os.Getenv(key)
	valueInt := defaultVal

	if valueString != "" {
		i, err := strconv.Atoi(key)

		if err != nil {
			logfWarn("Failed to parse env property %s: %s is not "+
				"an integer. Using default.\n", key, valueString)
		} else {
			valueInt = i
		}
	}

	return valueInt
}

// Gets an environmental variable "key". If it does not exist, "defaultVal" is
// returned; if it does, it attempts to convert to a string slice, returning
// "defaultVal" is it fails.
func getStringArrayVar(key string, defaultVal string) []string {
	valueString := os.Getenv(key)

	if valueString == "" {
		valueString = defaultVal
	}

	valueSlice := splitDelimmitedString(valueString, stringListDelimitRegex)

	return valueSlice
}

// Splits a string on a regular expression.
func splitDelimmitedString(str string, regex string) []string {
	var result []string

	str = strings.TrimSpace(str)

	if str != "" {
		reg := regexp.MustCompile(regex)
		indices := reg.FindAllStringIndex(str, -1)

		result = make([]string, len(indices)+1)

		lastStart := 0
		for i, val := range indices {
			result[i] = str[lastStart:val[0]]
			lastStart = val[1]
		}

		result[len(indices)] = str[lastStart:]

		// Special case of single empty string
		if len(result) == 1 && result[0] == "" {
			result = make([]string, 0, 0)
		}
	}

	return result
}
<|endoftext|>


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

package service

import (
	"fmt"
	"io"
	"net"
	"time"

	"github.com/surgemq/message"
)

type netReader interface {
	io.Reader
	SetReadDeadline(t time.Time) error
}

type timeoutReader struct {
	d    time.Duration
	conn netReader
}

func (r timeoutReader) Read(b []byte) (int, error) {
	if err := r.conn.SetReadDeadline(time.Now().Add(r.d)); err != nil {
		return 0, err
	}
	return r.conn.Read(b)
}

// receiver() reads data from the network, and writes the data into the incoming buffer
func (this *service) receiver() {
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) receiver开始", this.cid())
	})*/
	defer func() {
		// Let's recover from panic
		if r := recover(); r != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("(%s) Recovering from panic: %v", this.cid(), r)
			})
		}

		this.wgStopped.Done()

		Log.Debugc(func() string {
			return fmt.Sprintf("(%s) Stopping receiver", this.cid())
		})
	}()

	//   Log.Debugc(func() string{ return fmt.Sprintf("(%s) Starting receiver", this.cid())})

	this.wgStarted.Done()

	switch conn := this.conn.(type) {
	case net.Conn:
		//Log.Debugc(func() string{ return fmt.Sprintf("server/handleConnection: Setting read deadline to %d", time.Second*time.Duration(this.keepAlive))})
		keepAlive := time.Second * time.Duration(this.keepAlive)
		r := timeoutReader{
			d:    keepAlive + (keepAlive / 2),
			conn: conn,
		}

		for {
			_, err := this.in.ReadFrom(r)
			//       Log.Errorc(func() string{ return fmt.Sprintf("this.sess is: %v", this.sess)})
			//       Log.Errorc(func() string{ return fmt.Sprintf("this.sessMgr is: %v", this.sessMgr)})

			/*if err != nil {
				Log.Infoc(func() string { return fmt.Sprintf("(%s) error reading from connection: %v", this.cid(), err) })
				//         if err != io.EOF {
				//         }
				return
			}*/
			if err != nil {

				if err == io.EOF {
					Log.Errorc(func() string {
						return fmt.Sprintf("(%s) error reading from connection: %v", this.cid(), err)
					})
					return
				} else {
					Log.Infoc(func() string {
						return fmt.Sprintf("(%s) info reading from connection: %v", this.cid(), err)
					})
				}

				continue
			} /*else {
				Log.Debugc(func() string {
					return fmt.Sprintf("(%s)向ringbuffer些数据成功！", this.cid())
				})
			}*/
		}

	//case *websocket.Conn:
	//	Log.Errorc(func() string{ return fmt.Sprintf("(%s) Websocket: %v", this.cid(), ErrInvalidConnectionType)})

	default:
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) %v", this.cid(), ErrInvalidConnectionType)
		})
	}
}

// sender() writes data from the outgoing buffer to the network
func (this *service) sender() {
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) sender开始", this.cid())
	})*/
	defer func() {
		// Let's recover from panic
		if r := recover(); r != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("(%s) Recovering from panic: %v", this.cid(), r)
			})
		}

		this.wgStopped.Done()

		Log.Debugc(func() string {
			return fmt.Sprintf("(%s) Stopping sender", this.cid())
		})
	}()

	//   Log.Debugc(func() string{ return fmt.Sprintf("(%s) Starting sender", this.cid())})

	this.wgStarted.Done()

	switch conn := this.conn.(type) {
	case net.Conn:
		for {
			_, err := this.out.WriteTo(conn)

			if err != nil {
				Log.Errorc(func() string {
					return fmt.Sprintf("(%s)Error Sender  writing data: %v", this.cid(), err)
				})
				return
			}
		}

	//case *websocket.Conn:
	//	Log.Errorc(func() string{ return fmt.Sprintf("(%s) Websocket not supported", this.cid())})

	default:
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) Invalid connection type", this.cid())
		})
	}
}

// peekMessageSize() reads, but not commits, enough bytes to determine the size of
// the next message and returns the type and size.
func (this *service) peekMessageSize() (message.Message, int, error) {
	//fmt.Println("(", this.cid(), ")", "this.in.buf=", this.in.buf)
	//fmt.Println("(", this.cid(), ")", "this.out.buf=", this.out.buf)
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("(%s) peekMessageSize开始", this.cid())
	//})
	if this.isDone() {
		return nil, 0, io.EOF
	}
	var (
		b   []byte
		err error
	)

	if this.in == nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize this.in is nil", this.cid())
		})
		err = ErrBufferNotReady
		return nil, 0, err
	}

	// Peek cnt bytes from the input buffer.
	b, err = this.in.ReadWait()
	if err != nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize this.in.ReadWait falure:%v", this.cid(), err)
		})
		return nil, 0, err
	}
	//total := int(remlen) + 1 + m
	mtype := message.MessageType((b)[0] >> 4)
	//return mtype, total, err
	var msg message.Message
	msg, err = mtype.New()
	if err != nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize mtype.New() falure:%v", this.cid(), err)
		})
		return nil, 0, err
	}
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) 开始创建对象(%s)", this.cid(), msg.Name())
	})*/
	_, err = msg.Decode(b)
	if err != nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize msg.Decode falure:%v", this.cid(), err)
		})
		return nil, 0, err
	}
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) peekMessageSize结束(%s)", this.cid(), msg.Name())
	})*/
	//fmt.Println("this.in.readwait=", msg.Len())
	return msg, len(b), err
}

// peekMessage() reads a message from the buffer, but the bytes are NOT committed.
// This means the buffer still thinks the bytes are not read yet.
/*func (this *service) peekMessage(mtype message.MessageType, total int) (message.Message, int, error) {
	var (
		b    []byte
		err  error
		i, n int
		msg  message.Message
	)

	if this.in == nil {
		return nil, 0, ErrBufferNotReady
	}

	// Peek until we get total bytes
	for i = 0; ; i++ {
		// Peek remlen bytes from the input buffer.
		b, err = this.in.ReadWait(total)
		if err != nil && err != ErrBufferInsufficientData {
			return nil, 0, err
		}

		// If not enough bytes are returned, then continue until there's enough.
		if len(b) >= total {
			break
		}
	}

	msg, err = mtype.New()
	if err != nil {
		return nil, 0, err
	}

	n, err = msg.Decode(b)
	return msg, n, err
}*/

// readMessage() reads and copies a message from the buffer. The buffer bytes are
// committed as a result of the read.
/*
func (this *service) readMessage(mtype message.MessageType, total int) (message.Message, int, error) {
	var (
		b   []byte
		err error
		n   int
		msg message.Message
	)

	if this.in == nil {
		err = ErrBufferNotReady
		return nil, 0, err
	}

	if len(this.intmp) < total {
		this.intmp = make([]byte, total)
	}

	// Read until we get total bytes
	//l := 0
	//for l < total {
	n, err = this.in.Read(this.intmp[0:])
	//l += n
	Log.Debugc(func() string {
		return fmt.Sprintf("read %d bytes", n)
	})
	if err != nil {
		return nil, 0, err
	}
	//}

	b = this.intmp[:total]

	msg, err = mtype.New()
	if err != nil {
		return msg, 0, err
	}

	n, err = msg.Decode(b)
	return msg, n, err
}
*/

// writeMessage() writes a message to the outgoing buffer
func (this *service) writeMessage(msg message.Message) (int, error) {
	var (
		l     int = msg.Len()
		m, n  int
		err   error
		buf   []byte
		start int64
	//wrap bool
	)

	if this.out == nil {
		return 0, ErrBufferNotReady
	}

	// This is to serialize writes to the underlying buffer. Multiple goroutines could
	// potentially get here because of calling Publish() or Subscribe() or other
	// functions that will send messages. For example, if a message is received in
	// another connetion, and the message needs to be published to this client, then
	// the Publish() function is called, and at the same time, another client could
	// do exactly the same thing.
	//
	// Not an ideal fix though. If possible we should remove mutex and be lockfree.
	// Mainly because when there's a large number of goroutines that want to publish
	// to this client, then they will all block. However, this will do for now.
	//
	// FIXME: Try to find a better way than a mutex...if possible.
	this.wmu.Lock()
	defer this.wmu.Unlock()

	//buf, wrap, err = this.out.WriteWait(l)//zheliyouwenti
	start, _, err = this.out.waitForWriteSpace(l)
	if err != nil {
		return 0, err
	}
	buf = make([]byte, l)
	n, err = msg.Encode(buf[0:])
	if err != nil {
		return 0, err
	}
	index := start & this.out.mask
	this.out.buf[index] = ByteArray{index: index, bArray: buf}
	m, err = this.out.WriteCommit(n)
	if err != nil {
		return 0, err
	}

	this.outStat.increment(int64(m))

	return m, nil
}
<commit_msg>修改buffer读写限制<commit_after>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"fmt"
	"io"
	"net"
	"time"

	"github.com/surgemq/message"
)

type netReader interface {
	io.Reader
	SetReadDeadline(t time.Time) error
}

type timeoutReader struct {
	d    time.Duration
	conn netReader
}

func (r timeoutReader) Read(b []byte) (int, error) {
	if err := r.conn.SetReadDeadline(time.Now().Add(r.d)); err != nil {
		return 0, err
	}
	return r.conn.Read(b)
}

// receiver() reads data from the network, and writes the data into the incoming buffer
func (this *service) receiver() {
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) receiver开始", this.cid())
	})*/
	defer func() {
		// Let's recover from panic
		if r := recover(); r != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("(%s) Recovering from panic: %v", this.cid(), r)
			})
		}

		this.wgStopped.Done()

		Log.Debugc(func() string {
			return fmt.Sprintf("(%s) Stopping receiver", this.cid())
		})
	}()

	//   Log.Debugc(func() string{ return fmt.Sprintf("(%s) Starting receiver", this.cid())})

	this.wgStarted.Done()

	switch conn := this.conn.(type) {
	case net.Conn:
		//Log.Debugc(func() string{ return fmt.Sprintf("server/handleConnection: Setting read deadline to %d", time.Second*time.Duration(this.keepAlive))})
		keepAlive := time.Second * time.Duration(this.keepAlive)
		r := timeoutReader{
			d:    keepAlive + (keepAlive / 2),
			conn: conn,
		}

		for {
			_, err := this.in.ReadFrom(r)
			//       Log.Errorc(func() string{ return fmt.Sprintf("this.sess is: %v", this.sess)})
			//       Log.Errorc(func() string{ return fmt.Sprintf("this.sessMgr is: %v", this.sessMgr)})

			/*if err != nil {
				Log.Infoc(func() string { return fmt.Sprintf("(%s) error reading from connection: %v", this.cid(), err) })
				//         if err != io.EOF {
				//         }
				return
			}*/
			if err != nil {

				if err == io.EOF {
					Log.Errorc(func() string {
						return fmt.Sprintf("(%s) error reading from connection: %v", this.cid(), err)
					})
					return
				} else {
					Log.Infoc(func() string {
						return fmt.Sprintf("(%s) info reading from connection: %v", this.cid(), err)
					})
				}

				continue
			} /*else {
				Log.Debugc(func() string {
					return fmt.Sprintf("(%s)向ringbuffer些数据成功！", this.cid())
				})
			}*/
		}

	//case *websocket.Conn:
	//	Log.Errorc(func() string{ return fmt.Sprintf("(%s) Websocket: %v", this.cid(), ErrInvalidConnectionType)})

	default:
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) %v", this.cid(), ErrInvalidConnectionType)
		})
	}
}

// sender() writes data from the outgoing buffer to the network
func (this *service) sender() {
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) sender开始", this.cid())
	})*/
	defer func() {
		// Let's recover from panic
		/*if r := recover(); r != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("(%s) Recovering from panic: %v", this.cid(), r)
			})
		}*/

		this.wgStopped.Done()

		/*og.Debugc(func() string {
			return fmt.Sprintf("(%s) Stopping sender", this.cid())
		})*/
	}()

	//   Log.Debugc(func() string{ return fmt.Sprintf("(%s) Starting sender", this.cid())})

	this.wgStarted.Done()

	switch conn := this.conn.(type) {
	case net.Conn:
		for {
			_, err := this.out.WriteTo(conn)

			if err != nil {
				Log.Errorc(func() string {
					return fmt.Sprintf("(%s)Error Sender  writing data: %v", this.cid(), err)
				})
				return
			}
		}

	//case *websocket.Conn:
	//	Log.Errorc(func() string{ return fmt.Sprintf("(%s) Websocket not supported", this.cid())})

	default:
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) Invalid connection type", this.cid())
		})
	}
}

// peekMessageSize() reads, but not commits, enough bytes to determine the size of
// the next message and returns the type and size.
func (this *service) peekMessageSize() (message.Message, int, error) {
	//fmt.Println("(", this.cid(), ")", "this.in.buf=", this.in.buf)
	//fmt.Println("(", this.cid(), ")", "this.out.buf=", this.out.buf)
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("(%s) peekMessageSize开始", this.cid())
	//})
	if this.isDone() {
		return nil, 0, io.EOF
	}
	var (
		b   []byte
		err error
	)

	if this.in == nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize this.in is nil", this.cid())
		})
		err = ErrBufferNotReady
		return nil, 0, err
	}

	// Peek cnt bytes from the input buffer.
	b, err = this.in.ReadWait()
	if err != nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize this.in.ReadWait falure:%v", this.cid(), err)
		})
		return nil, 0, err
	}
	//total := int(remlen) + 1 + m
	mtype := message.MessageType((b)[0] >> 4)
	//return mtype, total, err
	var msg message.Message
	msg, err = mtype.New()
	if err != nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize mtype.New() falure:%v", this.cid(), err)
		})
		return nil, 0, err
	}
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) 开始创建对象(%s)", this.cid(), msg.Name())
	})*/
	_, err = msg.Decode(b)
	if err != nil {
		Log.Errorc(func() string {
			return fmt.Sprintf("(%s) peekMessageSize msg.Decode falure:%v", this.cid(), err)
		})
		return nil, 0, err
	}
	/*Log.Debugc(func() string {
		return fmt.Sprintf("(%s) peekMessageSize结束(%s)", this.cid(), msg.Name())
	})*/
	//fmt.Println("this.in.readwait=", msg.Len())
	return msg, len(b), err
}

// peekMessage() reads a message from the buffer, but the bytes are NOT committed.
// This means the buffer still thinks the bytes are not read yet.
/*func (this *service) peekMessage(mtype message.MessageType, total int) (message.Message, int, error) {
	var (
		b    []byte
		err  error
		i, n int
		msg  message.Message
	)

	if this.in == nil {
		return nil, 0, ErrBufferNotReady
	}

	// Peek until we get total bytes
	for i = 0; ; i++ {
		// Peek remlen bytes from the input buffer.
		b, err = this.in.ReadWait(total)
		if err != nil && err != ErrBufferInsufficientData {
			return nil, 0, err
		}

		// If not enough bytes are returned, then continue until there's enough.
		if len(b) >= total {
			break
		}
	}

	msg, err = mtype.New()
	if err != nil {
		return nil, 0, err
	}

	n, err = msg.Decode(b)
	return msg, n, err
}*/

// readMessage() reads and copies a message from the buffer. The buffer bytes are
// committed as a result of the read.
/*
func (this *service) readMessage(mtype message.MessageType, total int) (message.Message, int, error) {
	var (
		b   []byte
		err error
		n   int
		msg message.Message
	)

	if this.in == nil {
		err = ErrBufferNotReady
		return nil, 0, err
	}

	if len(this.intmp) < total {
		this.intmp = make([]byte, total)
	}

	// Read until we get total bytes
	//l := 0
	//for l < total {
	n, err = this.in.Read(this.intmp[0:])
	//l += n
	Log.Debugc(func() string {
		return fmt.Sprintf("read %d bytes", n)
	})
	if err != nil {
		return nil, 0, err
	}
	//}

	b = this.intmp[:total]

	msg, err = mtype.New()
	if err != nil {
		return msg, 0, err
	}

	n, err = msg.Decode(b)
	return msg, n, err
}
*/

// writeMessage() writes a message to the outgoing buffer
func (this *service) writeMessage(msg message.Message) (int, error) {
	var (
		l     int = msg.Len()
		m, n  int
		err   error
		buf   []byte
		start int64
	//wrap bool
	)

	if this.out == nil {
		return 0, ErrBufferNotReady
	}

	// This is to serialize writes to the underlying buffer. Multiple goroutines could
	// potentially get here because of calling Publish() or Subscribe() or other
	// functions that will send messages. For example, if a message is received in
	// another connetion, and the message needs to be published to this client, then
	// the Publish() function is called, and at the same time, another client could
	// do exactly the same thing.
	//
	// Not an ideal fix though. If possible we should remove mutex and be lockfree.
	// Mainly because when there's a large number of goroutines that want to publish
	// to this client, then they will all block. However, this will do for now.
	//
	// FIXME: Try to find a better way than a mutex...if possible.
	this.wmu.Lock()
	defer this.wmu.Unlock()

	//buf, wrap, err = this.out.WriteWait(l)//zheliyouwenti
	start, _, err = this.out.waitForWriteSpace(l)
	if err != nil {
		return 0, err
	}
	buf = make([]byte, l)
	n, err = msg.Encode(buf[0:])
	if err != nil {
		return 0, err
	}
	index := start & this.out.mask
	this.out.buf[index] = ByteArray{index: index, bArray: buf}
	m, err = this.out.WriteCommit(n)
	if err != nil {
		return 0, err
	}

	this.outStat.increment(int64(m))

	return m, nil
}
<|endoftext|>