<commit_before>package bild

import (
	"image"
	"image/color"
	"image/draw"
	"math"
)

// CloneAsRGBA returns an RGBA copy of the image
func CloneAsRGBA(src image.Image) *image.RGBA {
	bounds := src.Bounds()
	img := image.NewRGBA(bounds)
	draw.Draw(img, bounds, src, bounds.Min, draw.Src)
	return img
}

// Apply returns a copy of the image after applying a color function
// to each pixel on an image
func apply(img image.Image, fn func(color.RGBA) color.RGBA) *image.RGBA {
	bounds := img.Bounds()
	dst := CloneAsRGBA(img)
	w, h := bounds.Max.X, bounds.Max.Y

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {
				dst.Set(x, y, fn(dst.RGBAAt(x, y)))
			}
		}
	})

	return dst
}

func clamp(value, min, max float64) float64 {
	if value > max {
		return max
	}
	if value < min {
		return min
	}
	return value
}

func gaussianFunc(x, y, sigma float64) float64 {
	return math.Exp(-(x*x/sigma + y*y/sigma))
}

func quicksortRGBA(data []color.RGBA, min, max int) {
	if min > max {
		return
	}
	p := partitionRGBASlice(data, min, max)
	quicksortRGBA(data, min, p-1)
	quicksortRGBA(data, p+1, max)
}

func partitionRGBASlice(data []color.RGBA, min, max int) int {
	pivot := data[max]
	i := min
	for j := min; j < max; j++ {
		if rank(data[j]) <= rank(pivot) {
			temp := data[i]
			data[i] = data[j]
			data[j] = temp
			i++
		}
	}
	temp := data[i]
	data[i] = data[max]
	data[max] = temp
	return i
}

// Rank a color based on a color perception heuristic
func rank(c color.RGBA) float64 {
	return float64(c.R)*0.3 + float64(c.G)*0.6 + float64(c.B)*0.1
}
<commit_msg>added clamp helpers for uint8<commit_after>package bild

import (
	"image"
	"image/color"
	"image/draw"
	"math"
)

// CloneAsRGBA returns an RGBA copy of the image
func CloneAsRGBA(src image.Image) *image.RGBA {
	bounds := src.Bounds()
	img := image.NewRGBA(bounds)
	draw.Draw(img, bounds, src, bounds.Min, draw.Src)
	return img
}

// Apply returns a copy of the image after applying a color function
// to each pixel on an image
func apply(img image.Image, fn func(color.RGBA) color.RGBA) *image.RGBA {
	bounds := img.Bounds()
	dst := CloneAsRGBA(img)
	w, h := bounds.Max.X, bounds.Max.Y

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {
				dst.Set(x, y, fn(dst.RGBAAt(x, y)))
			}
		}
	})

	return dst
}

func clampUint8(x, min, max uint8) uint8 {
	if x < min {
		return min
	} else if x > max {
		return max
	}
	return x
}

func clampFloat64(value, min, max float64) float64 {
	if value > max {
		return max
	}
	if value < min {
		return min
	}
	return value
}

func gaussianFunc(x, y, sigma float64) float64 {
	return math.Exp(-(x*x/sigma + y*y/sigma))
}

func quicksortRGBA(data []color.RGBA, min, max int) {
	if min > max {
		return
	}
	p := partitionRGBASlice(data, min, max)
	quicksortRGBA(data, min, p-1)
	quicksortRGBA(data, p+1, max)
}

func partitionRGBASlice(data []color.RGBA, min, max int) int {
	pivot := data[max]
	i := min
	for j := min; j < max; j++ {
		if rank(data[j]) <= rank(pivot) {
			temp := data[i]
			data[i] = data[j]
			data[j] = temp
			i++
		}
	}
	temp := data[i]
	data[i] = data[max]
	data[max] = temp
	return i
}

// Rank a color based on a color perception heuristic
func rank(c color.RGBA) float64 {
	return float64(c.R)*0.3 + float64(c.G)*0.6 + float64(c.B)*0.1
}
<|endoftext|>


<commit_before>//go:build linux
// +build linux

/*
   Copyright The containerd 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 integration

import (
	"bytes"
	"io/ioutil"
	"strings"
	"testing"

	"github.com/containerd/cgroups"
	cgroupsv2 "github.com/containerd/cgroups/v2"
	"github.com/containerd/containerd"
	runtimespec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)

func checkMemoryLimit(t *testing.T, spec *runtimespec.Spec, memLimit int64) {
	require.NotNil(t, spec)
	require.NotNil(t, spec.Linux)
	require.NotNil(t, spec.Linux.Resources)
	require.NotNil(t, spec.Linux.Resources.Memory)
	require.NotNil(t, spec.Linux.Resources.Memory.Limit)
	assert.Equal(t, memLimit, *spec.Linux.Resources.Memory.Limit)
}

func checkMemorySwapLimit(t *testing.T, spec *runtimespec.Spec, memLimit *int64) {
	require.NotNil(t, spec)
	require.NotNil(t, spec.Linux)
	require.NotNil(t, spec.Linux.Resources)
	require.NotNil(t, spec.Linux.Resources.Memory)
	if memLimit == nil {
		require.Nil(t, spec.Linux.Resources.Memory.Swap)
	} else {
		require.NotNil(t, spec.Linux.Resources.Memory.Swap)
		assert.Equal(t, *memLimit, *spec.Linux.Resources.Memory.Swap)
	}
}

func getCgroupSwapLimitForTask(t *testing.T, task containerd.Task) uint64 {
	if cgroups.Mode() == cgroups.Unified {
		groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
		if err != nil {
			t.Fatal(err)
		}
		cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
		if err != nil {
			t.Fatal(err)
		}
		stat, err := cgroup2.Stat()
		if err != nil {
			t.Fatal(err)
		}
		return stat.Memory.SwapLimit
	}
	cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
	if err != nil {
		t.Fatal(err)
	}
	stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
	if err != nil {
		t.Fatal(err)
	}
	return stat.Memory.HierarchicalSwapLimit
}

func getCgroupMemoryLimitForTask(t *testing.T, task containerd.Task) uint64 {
	if cgroups.Mode() == cgroups.Unified {
		groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
		if err != nil {
			t.Fatal(err)
		}
		cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
		if err != nil {
			t.Fatal(err)
		}
		stat, err := cgroup2.Stat()
		if err != nil {
			t.Fatal(err)
		}
		return stat.Memory.UsageLimit
	}

	cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
	if err != nil {
		t.Fatal(err)
	}
	stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
	if err != nil {
		t.Fatal(err)
	}
	return stat.Memory.Usage.Limit
}

func isSwapLikelyEnabled() bool {
	// Check whether swap is enabled.
	swapFile := "/proc/swaps"
	swapData, err := ioutil.ReadFile(swapFile)
	if err != nil {
		// We can't read the file or it doesn't exist, assume we don't have swap.
		return false
	}

	swapData = bytes.TrimSpace(swapData) // extra trailing \n
	swapLines := strings.Split(string(swapData), "\n")
	// If there is more than one line (table headers) in /proc/swaps, swap is enabled
	return len(swapLines) > 1
}

func TestUpdateContainerResources_MemorySwap(t *testing.T) {
	if !isSwapLikelyEnabled() {
		t.Skipf("Swap is not enabled, or /proc/swaps is not readable. Swap is required for this test")
		return
	}

	t.Log("Create a sandbox")
	sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-swap-resources")

	EnsureImageExists(t, pauseImage)

	memoryLimit := int64(128 * 1024 * 1024)
	baseSwapLimit := int64(200 * 1024 * 1024)
	increasedSwapLimit := int64(256 * 1024 * 1024)

	expectedBaseSwap := baseSwapLimit
	expectedIncreasedSwap := increasedSwapLimit

	if cgroups.Mode() == cgroups.Unified {
		expectedBaseSwap = baseSwapLimit - memoryLimit
		expectedIncreasedSwap = increasedSwapLimit - memoryLimit
	}

	t.Log("Create a container with memory limit but no swap")
	cnConfig := ContainerConfig(
		"container",
		pauseImage,
		WithResources(&runtime.LinuxContainerResources{
			MemoryLimitInBytes:     memoryLimit,
			MemorySwapLimitInBytes: baseSwapLimit,
		}),
	)
	cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	container, err := containerdClient.LoadContainer(context.Background(), cn)
	require.NoError(t, err)
	spec, err := container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, memoryLimit)
	checkMemorySwapLimit(t, spec, &expectedBaseSwap)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	sw1 := baseSwapLimit
	checkMemorySwapLimit(t, spec, &sw1)

	t.Log("Start the container")
	require.NoError(t, runtimeService.StartContainer(cn))
	task, err := container.Task(context.Background(), nil)
	require.NoError(t, err)

	t.Log("Check memory limit in cgroup")
	memLimit := getCgroupMemoryLimitForTask(t, task)
	swapLimit := getCgroupSwapLimitForTask(t, task)
	assert.Equal(t, uint64(memoryLimit), memLimit)
	assert.Equal(t, uint64(expectedBaseSwap), swapLimit)

	t.Log("Update container memory limit after started")
	err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
		MemorySwapLimitInBytes: increasedSwapLimit,
	}, nil)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	checkMemorySwapLimit(t, spec, &increasedSwapLimit)

	t.Log("Check memory limit in cgroup")
	swapLimit = getCgroupSwapLimitForTask(t, task)
	assert.Equal(t, uint64(expectedIncreasedSwap), swapLimit)
}

func TestUpdateContainerResources_MemoryLimit(t *testing.T) {
	// TODO(claudiub): Make this test work once https://github.com/microsoft/hcsshim/pull/931 merges.
	t.Log("Create a sandbox")
	sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-resources")

	EnsureImageExists(t, pauseImage)

	t.Log("Create a container with memory limit")
	cnConfig := ContainerConfig(
		"container",
		pauseImage,
		WithResources(&runtime.LinuxContainerResources{
			MemoryLimitInBytes: 200 * 1024 * 1024,
		}),
	)
	cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	container, err := containerdClient.LoadContainer(context.Background(), cn)
	require.NoError(t, err)
	spec, err := container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, 200*1024*1024)

	t.Log("Update container memory limit after created")
	err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
		MemoryLimitInBytes: 400 * 1024 * 1024,
	}, nil)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, 400*1024*1024)

	t.Log("Start the container")
	require.NoError(t, runtimeService.StartContainer(cn))
	task, err := container.Task(context.Background(), nil)
	require.NoError(t, err)

	t.Log("Check memory limit in cgroup")
	cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
	require.NoError(t, err)
	stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
	require.NoError(t, err)
	assert.Equal(t, uint64(400*1024*1024), stat.Memory.Usage.Limit)

	t.Log("Update container memory limit after started")
	err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
		MemoryLimitInBytes: 800 * 1024 * 1024,
	}, nil)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, 800*1024*1024)

	t.Log("Check memory limit in cgroup")
	stat, err = cgroup.Stat(cgroups.IgnoreNotExist)
	require.NoError(t, err)
	assert.Equal(t, uint64(800*1024*1024), stat.Memory.Usage.Limit)
}
<commit_msg>fixup: check for swap accounting<commit_after>//go:build linux
// +build linux

/*
   Copyright The containerd 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 integration

import (
	"bytes"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/containerd/cgroups"
	cgroupsv2 "github.com/containerd/cgroups/v2"
	"github.com/containerd/containerd"
	runtimespec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)

func checkMemoryLimit(t *testing.T, spec *runtimespec.Spec, memLimit int64) {
	require.NotNil(t, spec)
	require.NotNil(t, spec.Linux)
	require.NotNil(t, spec.Linux.Resources)
	require.NotNil(t, spec.Linux.Resources.Memory)
	require.NotNil(t, spec.Linux.Resources.Memory.Limit)
	assert.Equal(t, memLimit, *spec.Linux.Resources.Memory.Limit)
}

func checkMemorySwapLimit(t *testing.T, spec *runtimespec.Spec, memLimit *int64) {
	require.NotNil(t, spec)
	require.NotNil(t, spec.Linux)
	require.NotNil(t, spec.Linux.Resources)
	require.NotNil(t, spec.Linux.Resources.Memory)
	if memLimit == nil {
		require.Nil(t, spec.Linux.Resources.Memory.Swap)
	} else {
		require.NotNil(t, spec.Linux.Resources.Memory.Swap)
		assert.Equal(t, *memLimit, *spec.Linux.Resources.Memory.Swap)
	}
}

func getCgroupSwapLimitForTask(t *testing.T, task containerd.Task) uint64 {
	if cgroups.Mode() == cgroups.Unified {
		groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
		if err != nil {
			t.Fatal(err)
		}
		cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
		if err != nil {
			t.Fatal(err)
		}
		stat, err := cgroup2.Stat()
		if err != nil {
			t.Fatal(err)
		}
		return stat.Memory.SwapLimit
	}
	cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
	if err != nil {
		t.Fatal(err)
	}
	stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
	if err != nil {
		t.Fatal(err)
	}
	return stat.Memory.HierarchicalSwapLimit
}

func getCgroupMemoryLimitForTask(t *testing.T, task containerd.Task) uint64 {
	if cgroups.Mode() == cgroups.Unified {
		groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
		if err != nil {
			t.Fatal(err)
		}
		cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
		if err != nil {
			t.Fatal(err)
		}
		stat, err := cgroup2.Stat()
		if err != nil {
			t.Fatal(err)
		}
		return stat.Memory.UsageLimit
	}

	cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
	if err != nil {
		t.Fatal(err)
	}
	stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
	if err != nil {
		t.Fatal(err)
	}
	return stat.Memory.Usage.Limit
}

func isSwapLikelyEnabled() bool {
	// Check whether swap is enabled.
	swapFile := "/proc/swaps"
	swapData, err := ioutil.ReadFile(swapFile)
	if err != nil {
		// We can't read the file or it doesn't exist, assume we don't have swap.
		return false
	}

	swapData = bytes.TrimSpace(swapData) // extra trailing \n
	swapLines := strings.Split(string(swapData), "\n")

	// If there is more than one line (table headers) in /proc/swaps, swap is enabled
	if len(swapLines) <= 1 {
		return false
	}

	// Linux Kernel's prior to 5.8 can disable swap accounting and is disabled
	// by default on Ubuntu. Most systems that run with cgroupsv2 enabled likely
	// have swap accounting enabled, here we assume that is true when running with
	// cgroupsv2 and check on cgroupsv1.
	if cgroups.Mode() == cgroups.Unified {
		return true
	}

	_, err = os.Stat("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes")
	// Assume any error means this test can't run for now.
	return err == nil
}

func TestUpdateContainerResources_MemorySwap(t *testing.T) {
	if !isSwapLikelyEnabled() {
		t.Skipf("Swap or swap accounting are not enabled. Swap is required for this test")
		return
	}

	t.Log("Create a sandbox")
	sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-swap-resources")

	EnsureImageExists(t, pauseImage)

	memoryLimit := int64(128 * 1024 * 1024)
	baseSwapLimit := int64(200 * 1024 * 1024)
	increasedSwapLimit := int64(256 * 1024 * 1024)

	expectedBaseSwap := baseSwapLimit
	expectedIncreasedSwap := increasedSwapLimit

	if cgroups.Mode() == cgroups.Unified {
		expectedBaseSwap = baseSwapLimit - memoryLimit
		expectedIncreasedSwap = increasedSwapLimit - memoryLimit
	}

	t.Log("Create a container with memory limit but no swap")
	cnConfig := ContainerConfig(
		"container",
		pauseImage,
		WithResources(&runtime.LinuxContainerResources{
			MemoryLimitInBytes:     memoryLimit,
			MemorySwapLimitInBytes: baseSwapLimit,
		}),
	)
	cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	container, err := containerdClient.LoadContainer(context.Background(), cn)
	require.NoError(t, err)
	spec, err := container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, memoryLimit)
	checkMemorySwapLimit(t, spec, &expectedBaseSwap)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	sw1 := baseSwapLimit
	checkMemorySwapLimit(t, spec, &sw1)

	t.Log("Start the container")
	require.NoError(t, runtimeService.StartContainer(cn))
	task, err := container.Task(context.Background(), nil)
	require.NoError(t, err)

	t.Log("Check memory limit in cgroup")
	memLimit := getCgroupMemoryLimitForTask(t, task)
	swapLimit := getCgroupSwapLimitForTask(t, task)
	assert.Equal(t, uint64(memoryLimit), memLimit)
	assert.Equal(t, uint64(expectedBaseSwap), swapLimit)

	t.Log("Update container memory limit after started")
	err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
		MemorySwapLimitInBytes: increasedSwapLimit,
	}, nil)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	checkMemorySwapLimit(t, spec, &increasedSwapLimit)

	t.Log("Check memory limit in cgroup")
	swapLimit = getCgroupSwapLimitForTask(t, task)
	assert.Equal(t, uint64(expectedIncreasedSwap), swapLimit)
}

func TestUpdateContainerResources_MemoryLimit(t *testing.T) {
	// TODO(claudiub): Make this test work once https://github.com/microsoft/hcsshim/pull/931 merges.
	t.Log("Create a sandbox")
	sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-resources")

	EnsureImageExists(t, pauseImage)

	t.Log("Create a container with memory limit")
	cnConfig := ContainerConfig(
		"container",
		pauseImage,
		WithResources(&runtime.LinuxContainerResources{
			MemoryLimitInBytes: 200 * 1024 * 1024,
		}),
	)
	cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	container, err := containerdClient.LoadContainer(context.Background(), cn)
	require.NoError(t, err)
	spec, err := container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, 200*1024*1024)

	t.Log("Update container memory limit after created")
	err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
		MemoryLimitInBytes: 400 * 1024 * 1024,
	}, nil)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, 400*1024*1024)

	t.Log("Start the container")
	require.NoError(t, runtimeService.StartContainer(cn))
	task, err := container.Task(context.Background(), nil)
	require.NoError(t, err)

	t.Log("Check memory limit in cgroup")
	cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
	require.NoError(t, err)
	stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
	require.NoError(t, err)
	assert.Equal(t, uint64(400*1024*1024), stat.Memory.Usage.Limit)

	t.Log("Update container memory limit after started")
	err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
		MemoryLimitInBytes: 800 * 1024 * 1024,
	}, nil)
	require.NoError(t, err)

	t.Log("Check memory limit in container OCI spec")
	spec, err = container.Spec(context.Background())
	require.NoError(t, err)
	checkMemoryLimit(t, spec, 800*1024*1024)

	t.Log("Check memory limit in cgroup")
	stat, err = cgroup.Stat(cgroups.IgnoreNotExist)
	require.NoError(t, err)
	assert.Equal(t, uint64(800*1024*1024), stat.Memory.Usage.Limit)
}
<|endoftext|>


<commit_before>package wundergo_test

import (
	"log"
	"os"
	"time"

	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/robdimsdale/wundergo"
)

const (
	SERVER_CONSISTENCY_TIMEOUT = 30 * time.Second
	POLLING_INTERVAL           = 10 * time.Millisecond
)

var (
	client wundergo.Client
)

func listContains(lists *[]wundergo.List, list *wundergo.List) bool {
	for _, l := range *lists {
		if l == *list {
			return true
		}
	}
	return false
}

func taskContains(tasks *[]wundergo.Task, task *wundergo.Task) bool {
	for _, t := range *tasks {
		if t == *task {
			return true
		}
	}
	return false
}

var _ = Describe("Wundergo library", func() {
	BeforeEach(func() {
		accessToken := os.Getenv("WL_ACCESS_TOKEN")
		clientID := os.Getenv("WL_CLIENT_ID")

		if accessToken == "" {
			log.Fatal("Error - WL_ACCESS_TOKEN must be provided")
		}

		if clientID == "" {
			log.Fatal("Error - WL_CLIENT_ID must be provided")
		}

		client = wundergo.NewOauthClient(accessToken, clientID)
	})

	Describe("Basic list functionality", func() {
		It("performs CRUD for lists", func() {
			uuid1, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			newListTitle1 := uuid1.String()

			uuid2, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			newListTitle2 := uuid2.String()

			var originalLists *[]wundergo.List
			Eventually(func() error {
				originalLists, err = client.Lists()
				return err
			}).ShouldNot(HaveOccurred())

			var newList *wundergo.List
			Eventually(func() error {
				newList, err = client.CreateList(newListTitle1)
				return err
			}).ShouldNot(HaveOccurred())

			var newLists *[]wundergo.List
			Eventually(func() error {
				newLists, err = client.Lists()
				return err
			}).ShouldNot(HaveOccurred())
			Expect(listContains(newLists, newList)).To(BeTrue())

			newList.Title = newListTitle2
			var updatedList *wundergo.List
			Eventually(func() error {
				updatedList, err = client.UpdateList(*newList)
				return err
			}).ShouldNot(HaveOccurred())
			newList.Revision = newList.Revision + 1
			Expect(updatedList).To(Equal(newList))

			Eventually(func() error {
				newList, err = client.List(newList.ID)
				return err
			}).ShouldNot(HaveOccurred())

			Eventually(func() error {
				return client.DeleteList(*newList)
			}).ShouldNot(HaveOccurred())

			Eventually(func() *[]wundergo.List {
				afterDeleteLists, err := client.Lists()
				Expect(err).ShouldNot(HaveOccurred())
				return afterDeleteLists
			}, SERVER_CONSISTENCY_TIMEOUT, POLLING_INTERVAL).Should(Equal(originalLists))
		})
	})

	Describe("Basic task functionality", func() {
		It("can perform CRUD for tasks", func() {
			var lists []wundergo.List
			Eventually(func() error {
				l, err := client.Lists()
				lists = *l
				return err
			}).ShouldNot(HaveOccurred())
			list := lists[0]

			uuid, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			newTaskTitle := uuid.String()

			var originalTasks *[]wundergo.Task
			Eventually(func() error {
				originalTasks, err = client.TasksForListID(list.ID)
				return err
			}).ShouldNot(HaveOccurred())

			var task *wundergo.Task
			Eventually(func() error {
				task, err = client.CreateTask(
					newTaskTitle,
					list.ID,
					0,
					false,
					"",
					0,
					"1970-01-01",
					false,
				)
				return err
			}).ShouldNot(HaveOccurred())

			var completedTasks *[]wundergo.Task
			Eventually(func() error {
				completed := true
				completedTasks, err = client.CompletedTasksForListID(list.ID, completed)
				return err
			}).ShouldNot(HaveOccurred())
			Expect(taskContains(completedTasks, task)).To(BeFalse())

			task.DueDate = "1971-01-01"
			task.Completed = true
			Eventually(func() error {
				task, err = client.UpdateTask(*task)
				return err
			}).ShouldNot(HaveOccurred())

			Eventually(func() error {
				completed := true
				completedTasks, err = client.CompletedTasksForListID(list.ID, completed)
				return err
			}).ShouldNot(HaveOccurred())
			Expect(taskContains(completedTasks, task)).To(BeTrue())

			var note *wundergo.Note
			Eventually(func() error {
				note, err = client.CreateNote("myContent", task.ID)
				return err
			}).ShouldNot(HaveOccurred())

			note.Content = "newContent"
			Eventually(func() error {
				note, err = client.UpdateNote(*note)
				return err
			}).ShouldNot(HaveOccurred())

			Eventually(func() error {
				return client.DeleteNote(*note)
			}).ShouldNot(HaveOccurred())

			Eventually(func() error {
				task, err = client.Task(task.ID)
				return err
			}).ShouldNot(HaveOccurred())

			Eventually(func() error {
				return client.DeleteTask(*task)
			}).ShouldNot(HaveOccurred())

			Eventually(func() *[]wundergo.Task {
				afterDeleteTasks, err := client.TasksForListID(list.ID)
				Expect(err).ShouldNot(HaveOccurred())
				return afterDeleteTasks
			}, SERVER_CONSISTENCY_TIMEOUT, POLLING_INTERVAL).Should(Equal(originalTasks))
		})
	})
})
<commit_msg>Refactor integration tests.<commit_after>package wundergo_test

import (
	"log"
	"os"
	"time"

	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/robdimsdale/wundergo"
)

const (
	SERVER_CONSISTENCY_TIMEOUT = 30 * time.Second
	POLLING_INTERVAL           = 10 * time.Millisecond
)

var (
	client wundergo.Client
)

func listContains(lists *[]wundergo.List, list *wundergo.List) bool {
	for _, l := range *lists {
		if l == *list {
			return true
		}
	}
	return false
}

func taskContains(tasks *[]wundergo.Task, task *wundergo.Task) bool {
	for _, t := range *tasks {
		if t == *task {
			return true
		}
	}
	return false
}

var _ = Describe("Wundergo library", func() {
	BeforeEach(func() {
		accessToken := os.Getenv("WL_ACCESS_TOKEN")
		clientID := os.Getenv("WL_CLIENT_ID")

		if accessToken == "" {
			log.Fatal("Error - WL_ACCESS_TOKEN must be provided")
		}

		if clientID == "" {
			log.Fatal("Error - WL_CLIENT_ID must be provided")
		}

		client = wundergo.NewOauthClient(accessToken, clientID)
	})

	Describe("Basic list functionality", func() {
		It("performs CRUD for lists", func() {
			uuid1, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			newListTitle1 := uuid1.String()

			uuid2, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			newListTitle2 := uuid2.String()

			var originalLists *[]wundergo.List
			Eventually(func() error {
				originalLists, err = client.Lists()
				return err
			}).Should(Succeed())

			var newList *wundergo.List
			Eventually(func() error {
				newList, err = client.CreateList(newListTitle1)
				return err
			}).Should(Succeed())

			var newLists *[]wundergo.List
			Eventually(func() error {
				newLists, err = client.Lists()
				return err
			}).Should(Succeed())
			Expect(listContains(newLists, newList)).To(BeTrue())

			newList.Title = newListTitle2
			var updatedList *wundergo.List
			Eventually(func() error {
				updatedList, err = client.UpdateList(*newList)
				return err
			}).Should(Succeed())
			newList.Revision = newList.Revision + 1
			Expect(updatedList).To(Equal(newList))

			Eventually(func() error {
				newList, err = client.List(newList.ID)
				return err
			}).Should(Succeed())

			Eventually(func() error {
				return client.DeleteList(*newList)
			}).Should(Succeed())

			Eventually(client.Lists, SERVER_CONSISTENCY_TIMEOUT, POLLING_INTERVAL).Should(Equal(originalLists))
		})
	})

	Describe("Basic task functionality", func() {
		It("can perform CRUD for tasks", func() {
			var lists []wundergo.List
			Eventually(func() error {
				l, err := client.Lists()
				lists = *l
				return err
			}).Should(Succeed())
			list := lists[0]

			uuid, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			newTaskTitle := uuid.String()

			var originalTasks *[]wundergo.Task
			Eventually(func() error {
				originalTasks, err = client.TasksForListID(list.ID)
				return err
			}).ShouldNot(HaveOccurred())

			var task *wundergo.Task
			Eventually(func() error {
				task, err = client.CreateTask(
					newTaskTitle,
					list.ID,
					0,
					false,
					"",
					0,
					"1970-01-01",
					false,
				)
				return err
			}).ShouldNot(HaveOccurred())

			var completedTasks *[]wundergo.Task
			Eventually(func() error {
				completed := true
				completedTasks, err = client.CompletedTasksForListID(list.ID, completed)
				return err
			}).Should(Succeed())
			Expect(taskContains(completedTasks, task)).To(BeFalse())

			task.DueDate = "1971-01-01"
			task.Completed = true
			Eventually(func() error {
				task, err = client.UpdateTask(*task)
				return err
			}).Should(Succeed())

			Eventually(func() error {
				completed := true
				completedTasks, err = client.CompletedTasksForListID(list.ID, completed)
				return err
			}).ShouldNot(HaveOccurred())
			Expect(taskContains(completedTasks, task)).To(BeTrue())

			var note *wundergo.Note
			Eventually(func() error {
				note, err = client.CreateNote("myContent", task.ID)
				return err
			}).Should(Succeed())

			note.Content = "newContent"
			Eventually(func() error {
				note, err = client.UpdateNote(*note)
				return err
			}).Should(Succeed())

			Eventually(func() error {
				return client.DeleteNote(*note)
			}).Should(Succeed())

			Eventually(func() error {
				task, err = client.Task(task.ID)
				return err
			}).Should(Succeed())

			Eventually(func() error {
				return client.DeleteTask(*task)
			}).Should(Succeed())

			Eventually(func() (*[]wundergo.Task, error) {
				return client.TasksForListID(list.ID)
			}, SERVER_CONSISTENCY_TIMEOUT, POLLING_INTERVAL).Should(Equal(originalTasks))
		})
	})
})
<|endoftext|>


<commit_before>package convert

import (
	"github.com/stretchr/testify/require"
	"testing"
)

func TestToBool(t *testing.T) {
	v, err := ToBool("true")
	require.Nil(t, err)
	require.Equal(t, true, v)

	v, err = ToBool("1")
	require.Nil(t, err)
	require.Equal(t, true, v)

	v, err = ToBool("0")
	require.Nil(t, err)
	require.Equal(t, false, v)

	v, err = ToBool("false")
	require.Nil(t, err)
	require.Equal(t, false, v)

	v, err = ToBool("dsds3wr")
	require.NotNil(t, err)
}

func TestToInt(t *testing.T) {
	v, err := ToInt("1")
	require.Nil(t, err)
	require.Equal(t, 1, v)

	v, err = ToInt("-1")
	require.Nil(t, err)
	require.Equal(t, -1, v)

	v, err = ToInt("12345.12345")
	require.NotNil(t, err)

	v, err = ToInt("fgfert3")
	require.NotNil(t, err)
}

func TestToFloat(t *testing.T) {
	v, err := ToFloat("1")
	require.Nil(t, err)
	require.Equal(t, 1.0, v)

	v, err = ToFloat("-1")
	require.Nil(t, err)
	require.Equal(t, -1.0, v)

	v, err = ToFloat("12345.12345")
	require.Nil(t, err)
	require.Equal(t, 12345.12345, v)

	v, err = ToFloat("fgfert3")
	require.NotNil(t, err)
}

func TestToDate(t *testing.T) {
	v, err := ToDate("43308.7047106481")
	require.Nil(t, err)
	require.Equal(t, "2018-07-27 16:54:46 +0000 UTC", v.UTC().String())

	v, err = ToDate("2018-07-27T17:40:58")
	require.Nil(t, err)
	require.Equal(t, "2018-07-27 17:40:58 +0000 UTC", v.UTC().String())

	v, err = ToDate("dsdsds")
	require.NotNil(t, err)
}
<commit_msg>golint fixes<commit_after>package convert

import (
	"github.com/stretchr/testify/require"
	"testing"
)

func TestToBool(t *testing.T) {
	v, err := ToBool("true")
	require.Nil(t, err)
	require.Equal(t, true, v)

	v, err = ToBool("1")
	require.Nil(t, err)
	require.Equal(t, true, v)

	v, err = ToBool("0")
	require.Nil(t, err)
	require.Equal(t, false, v)

	v, err = ToBool("false")
	require.Nil(t, err)
	require.Equal(t, false, v)

	_, err = ToBool("dsds3wr")
	require.NotNil(t, err)
}

func TestToInt(t *testing.T) {
	v, err := ToInt("1")
	require.Nil(t, err)
	require.Equal(t, 1, v)

	v, err = ToInt("-1")
	require.Nil(t, err)
	require.Equal(t, -1, v)

	_, err = ToInt("12345.12345")
	require.NotNil(t, err)

	_, err = ToInt("fgfert3")
	require.NotNil(t, err)
}

func TestToFloat(t *testing.T) {
	v, err := ToFloat("1")
	require.Nil(t, err)
	require.Equal(t, 1.0, v)

	v, err = ToFloat("-1")
	require.Nil(t, err)
	require.Equal(t, -1.0, v)

	v, err = ToFloat("12345.12345")
	require.Nil(t, err)
	require.Equal(t, 12345.12345, v)

	_, err = ToFloat("fgfert3")
	require.NotNil(t, err)
}

func TestToDate(t *testing.T) {
	v, err := ToDate("43308.7047106481")
	require.Nil(t, err)
	require.Equal(t, "2018-07-27 16:54:46 +0000 UTC", v.UTC().String())

	v, err = ToDate("2018-07-27T17:40:58")
	require.Nil(t, err)
	require.Equal(t, "2018-07-27 17:40:58 +0000 UTC", v.UTC().String())

	_, err = ToDate("dsdsds")
	require.NotNil(t, err)
}
<|endoftext|>


<commit_before>package consensus

import (
	"sort"

	"github.com/rivine/rivine/encoding"
	"github.com/rivine/rivine/types"
)

// blockRuleHelper assists with block validity checks by calculating values
// on blocks that are relevant to validity rules.
type blockRuleHelper interface {
	minimumValidChildTimestamp(dbBucket, *processedBlock) types.Timestamp
}

// stdBlockRuleHelper is the standard implementation of blockRuleHelper.
type stdBlockRuleHelper struct {
	chainCts types.ChainConstants
}

// minimumValidChildTimestamp returns the earliest timestamp that a child node
// can have while still being valid. See section 'Block Timestamps' in
// Consensus.md.
//
// To boost performance, minimumValidChildTimestamp is passed a bucket that it
// can use from inside of a boltdb transaction.
func (rh stdBlockRuleHelper) minimumValidChildTimestamp(blockMap dbBucket, pb *processedBlock) types.Timestamp {
	// Get the previous MedianTimestampWindow timestamps.
	windowTimes := make(types.TimestampSlice, rh.chainCts.MedianTimestampWindow)
	windowTimes[0] = pb.Block.Timestamp
	parent := pb.Block.ParentID
	for i := uint64(1); i < rh.chainCts.MedianTimestampWindow; i++ {
		// If the genesis block is 'parent', use the genesis block timestamp
		// for all remaining times.
		if parent == (types.BlockID{}) {
			windowTimes[i] = windowTimes[i-1]
			continue
		}

		// Get the next parent's bytes. Because the ordering is specific, the
		// parent does not need to be decoded entirely to get the desired
		// information. This provides a performance boost. The id of the next
		// parent lies at the first 32 bytes, and the timestamp of the block
		// lies at bytes 40-48.
		parentBytes := blockMap.Get(parent[:])
		copy(parent[:], parentBytes[:32])
		windowTimes[i] = types.Timestamp(encoding.DecUint64(parentBytes[40:48]))
	}
	sort.Sort(windowTimes)

	// Return the median of the sorted timestamps.
	return windowTimes[len(windowTimes)/2]
}
<commit_msg>Fix #227: Make minimumValidChildTimestamp actually decode the timestamp<commit_after>package consensus

import (
	"sort"

	"github.com/rivine/rivine/encoding"
	"github.com/rivine/rivine/types"
)

// blockRuleHelper assists with block validity checks by calculating values
// on blocks that are relevant to validity rules.
type blockRuleHelper interface {
	minimumValidChildTimestamp(dbBucket, *processedBlock) types.Timestamp
}

// stdBlockRuleHelper is the standard implementation of blockRuleHelper.
type stdBlockRuleHelper struct {
	chainCts types.ChainConstants
}

// minimumValidChildTimestamp returns the earliest timestamp that a child node
// can have while still being valid. See section 'Block Timestamps' in
// Consensus.md.
//
// To boost performance, minimumValidChildTimestamp is passed a bucket that it
// can use from inside of a boltdb transaction.
func (rh stdBlockRuleHelper) minimumValidChildTimestamp(blockMap dbBucket, pb *processedBlock) types.Timestamp {
	// Get the previous MedianTimestampWindow timestamps.
	windowTimes := make(types.TimestampSlice, rh.chainCts.MedianTimestampWindow)
	windowTimes[0] = pb.Block.Timestamp
	parent := pb.Block.ParentID
	for i := uint64(1); i < rh.chainCts.MedianTimestampWindow; i++ {
		// If the genesis block is 'parent', use the genesis block timestamp
		// for all remaining times.
		if parent == (types.BlockID{}) {
			windowTimes[i] = windowTimes[i-1]
			continue
		}

		// Get the next parent's bytes. Because the ordering is specific, the
		// parent does not need to be decoded entirely to get the desired
		// information. This provides a performance boost. The id of the next
		// parent lies at the first 32 bytes, and the timestamp of the block
		// lies at bytes 32-40.
		parentBytes := blockMap.Get(parent[:])
		copy(parent[:], parentBytes[:32])
		windowTimes[i] = types.Timestamp(encoding.DecUint64(parentBytes[32:40]))
	}
	sort.Sort(windowTimes)

	// Return the median of the sorted timestamps.
	return windowTimes[len(windowTimes)/2]
}
<|endoftext|>


<commit_before>package corsproxy

import (
	"net/http"
	"net/http/httptest"
	"os"
	"reflect"
	"testing"
)

func TestValidateRequest(t *testing.T) {
	os.Setenv("ALLOWED_ORIGIN_REGEXP", "^https?:\\/\\/.*you\\.com")
	tests := []struct {
		title  string
		method string
		url    string
		origin string
		err    error
		status int
	}{
		{
			"Test missing origin header",
			"GET",
			"http:/test.com/?https://what.the.what",
			"",
			errMissingOrigin,
			http.StatusBadRequest,
		},
		{
			"Test missing query string",
			"GET",
			"http:/test.com/",
			"https://you.com",
			errMissingQuery,
			http.StatusBadRequest,
		},
		{
			"Test wrong VERB",
			"POST",
			"http:/test.com/?https://what.the.what",
			"https://you.com",
			errUnsupportedMethod,
			http.StatusMethodNotAllowed,
		},
		{
			"Test origin mismatch",
			"GET",
			"http:/test.com/?https://what.the.what",
			"https://me.com",
			errOriginMismatch,
			http.StatusBadRequest,
		},
		{
			"Test valid case",
			"GET",
			"http:/test.com/?https://what.the.what",
			"https://you.com",
			nil,
			http.StatusOK,
		},
	}

	for _, tt := range tests {
		r := httptest.NewRequest(tt.method, tt.url, nil)
		if tt.origin != "" {
			r.Header.Set("Origin", tt.origin)
		}
		wantErr, wantStatus := tt.err, tt.status
		if gotStatus, gotErr := validateRequest(r); gotErr != wantErr || gotStatus != wantStatus {
			t.Errorf("%s:  got err %v, want err %v\ngot status %d, want status %d", tt.title, gotErr, wantErr, gotStatus, wantStatus)
		}
	}
}

func TestWriteCorsHeaders(t *testing.T) {
	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "http:/test.com/?https://what.the.what", nil)
	rsp := http.Response{"200 OK", 200, "HTTP/1.1", 1, 1, http.Header{}, nil, -1, nil, true, true, nil, nil, nil}
	rsp.Header.Set("a", "b")
	r.Header.Set("Origin", "you")
	expectedHeaders := http.Header{}
	expectedHeaders.Set("A", "b")
	expectedHeaders.Set("Access-Control-Allow-Origin", "you")
	expectedHeaders.Set("Access-Control-Allow-Methods", "GET")
	expectedHeaders.Set("Access-Control-Max-Age", "86400")
	writeCorsHeaders(w, r, &rsp)
	eq := reflect.DeepEqual(w.Header(), expectedHeaders)
	if !eq {
		t.Errorf("expected headers %q != actual headers %q", expectedHeaders, w.Header())
	}
}
<commit_msg>Describe test data<commit_after>package corsproxy

import (
	"net/http"
	"net/http/httptest"
	"os"
	"reflect"
	"testing"
)

func TestValidateRequest(t *testing.T) {
	const (
		goodUrl     = "http:/test.com/?https://what.the.what"
		badUrl      = "http:/test.com/"
		goodOrigin  = "https://you.com"
		badOrigin   = "https://me.com"
		emptyOrigin = ""
	)
	os.Setenv("ALLOWED_ORIGIN_REGEXP", "^https?:\\/\\/.*you\\.com")
	tests := []struct {
		title  string
		method string
		url    string
		origin string
		err    error
		status int
	}{
		{
			"Test missing origin header",
			"GET",
			goodUrl,
			emptyOrigin,
			errMissingOrigin,
			http.StatusBadRequest,
		},
		{
			"Test missing query string",
			"GET",
			badUrl,
			goodOrigin,
			errMissingQuery,
			http.StatusBadRequest,
		},
		{
			"Test wrong VERB",
			"POST",
			goodUrl,
			goodOrigin,
			errUnsupportedMethod,
			http.StatusMethodNotAllowed,
		},
		{
			"Test origin mismatch",
			"GET",
			goodUrl,
			badOrigin,
			errOriginMismatch,
			http.StatusBadRequest,
		},
		{
			"Test valid case",
			"GET",
			goodUrl,
			goodOrigin,
			nil,
			http.StatusOK,
		},
	}

	for _, tt := range tests {
		r := httptest.NewRequest(tt.method, tt.url, nil)
		if tt.origin != "" {
			r.Header.Set("Origin", tt.origin)
		}
		wantErr, wantStatus := tt.err, tt.status
		if gotStatus, gotErr := validateRequest(r); gotErr != wantErr || gotStatus != wantStatus {
			t.Errorf("%s:  got err %v, want err %v\ngot status %d, want status %d", tt.title, gotErr, wantErr, gotStatus, wantStatus)
		}
	}
}

func TestWriteCorsHeaders(t *testing.T) {
	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "http:/test.com/?https://what.the.what", nil)
	rsp := http.Response{"200 OK", 200, "HTTP/1.1", 1, 1, http.Header{}, nil, -1, nil, true, true, nil, nil, nil}
	rsp.Header.Set("a", "b")
	r.Header.Set("Origin", "you")
	expectedHeaders := http.Header{}
	expectedHeaders.Set("A", "b")
	expectedHeaders.Set("Access-Control-Allow-Origin", "you")
	expectedHeaders.Set("Access-Control-Allow-Methods", "GET")
	expectedHeaders.Set("Access-Control-Max-Age", "86400")
	writeCorsHeaders(w, r, &rsp)
	eq := reflect.DeepEqual(w.Header(), expectedHeaders)
	if !eq {
		t.Errorf("expected headers %q != actual headers %q", expectedHeaders, w.Header())
	}
}
<|endoftext|>


<commit_before>package httpapi

import (
	"encoding/json"
	"fmt"
	"github.com/ActiveState/log"
	"io/ioutil"
	"net/http"
	"reflect"
)

// Handler handles a single API endpoint
type Handler struct {
	// RequestStruct is a struct to store the fields of request
	// paramemters, passed as JSON from the client.
	RequestStruct interface{}
}

// FIXME: best way to report errors in ServerHTTP?
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Infof("httpapi -- %+v", r)
	request := reflect.New(reflect.TypeOf(h.RequestStruct)).Interface().(RequestParams)

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Error(err)
		return
	}
	if err := json.Unmarshal(body, request); err != nil {
		log.Errorf("Error decoding JSON body in POST request (%s). Original body was: %s", err, string(body))
		return
	}

	response, err := request.HandleRequest()
	if err != nil {
		log.Errorf("Request error -- %s", err)
		http.Error(w, err.Error(), 500)
	} else {
		data, err := json.Marshal(response)
		if err != nil {
			err = fmt.Errorf("Error encoding response into JSON: %s", err)
			http.Error(w, err.Error(), 500)
		} else {
			_, err = w.Write(data)
			if err != nil {
				log.Errorf("Failed to write http response: %s", err)
			}
		}
	}
}

type RequestParams interface {
	// HandleRequest is called when a request comes in. POST body will
	// be decoded into the receiver; returned value will be encoded to
	// JSON before responding to the client.
	// FIXME: pre-define errors for appropriate HTTP codes (404, 500) ...
	HandleRequest() (interface{}, error)
}
<commit_msg>use a custom logger to use request-prefixes<commit_after>package httpapi

import (
	"encoding/json"
	"fmt"
	"github.com/ActiveState/log"
	"io/ioutil"
	"net/http"
	"reflect"
)

// Handler handles a single API endpoint
type Handler struct {
	// RequestStruct is a struct to store the fields of request
	// paramemters, passed as JSON from the client.
	RequestStruct interface{}
}

// FIXME: best way to report errors in ServerHTTP?
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Create a logger with a prefix unique to this request. Assuming
	// that a new request object is created per request, its memory
	// address (%p) should give us an unique identifier.
	l := log.New()
	l.SetPrefix(fmt.Sprintf("[HTTP:%p] ", r))

	l.Infof("%+v", r)
	request := reflect.New(reflect.TypeOf(h.RequestStruct)).Interface().(RequestParams)

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		l.Error(err)
		return
	}
	if err := json.Unmarshal(body, request); err != nil {
		l.Errorf("Unable to decode JSON body in POST request (%s). Original body was: %s", err, string(body))
		return
	}

	response, err := request.HandleRequest()
	if err != nil {
		l.Errorf("Failed to handle this request -- %s", err)
		http.Error(w, err.Error(), 500)
	} else {
		data, err := json.Marshal(response)
		if err != nil {
			err = fmt.Errorf("Unable to response into JSON: %s", err)
			http.Error(w, err.Error(), 500)
		} else {
			_, err = w.Write(data)
			if err != nil {
				l.Errorf("Unable to write http response: %s", err)
			}
		}
	}
}

type RequestParams interface {
	// HandleRequest is called when a request comes in. POST body will
	// be decoded into the receiver; returned value will be encoded to
	// JSON before responding to the client.
	// FIXME: pre-define errors for appropriate HTTP codes (404, 500) ...
	HandleRequest() (interface{}, error)
}
<|endoftext|>


<commit_before><commit_msg>Fix linting error from new version (#12234)<commit_after><|endoftext|>


<commit_before>// Copyright 2017 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 chunk

import (
	"encoding/binary"
	"math"
	"unsafe"

	"github.com/pingcap/parser/mysql"
	"github.com/pingcap/tidb/types"
	"github.com/pingcap/tidb/types/json"
	"github.com/pingcap/tidb/util/hack"
)

// MutRow represents a mutable Row.
// The underlying columns only contains one row and not exposed to the user.
type MutRow Row

// ToRow converts the MutRow to Row, so it can be used to read data.
func (mr MutRow) ToRow() Row {
	return Row(mr)
}

// Len returns the number of columns.
func (mr MutRow) Len() int {
	return len(mr.c.columns)
}

// MutRowFromValues creates a MutRow from a interface slice.
func MutRowFromValues(vals ...interface{}) MutRow {
	c := &Chunk{}
	for _, val := range vals {
		col := makeMutRowColumn(val)
		c.columns = append(c.columns, col)
	}
	return MutRow{c: c}
}

// MutRowFromDatums creates a MutRow from a datum slice.
func MutRowFromDatums(datums []types.Datum) MutRow {
	c := &Chunk{}
	for _, d := range datums {
		col := makeMutRowColumn(d.GetValue())
		c.columns = append(c.columns, col)
	}
	return MutRow{c: c, idx: 0}
}

// MutRowFromTypes creates a MutRow from a FieldType slice, each column is initialized to zero value.
func MutRowFromTypes(types []*types.FieldType) MutRow {
	c := &Chunk{}
	for _, tp := range types {
		col := makeMutRowColumn(zeroValForType(tp))
		c.columns = append(c.columns, col)
	}
	return MutRow{c: c, idx: 0}
}

func zeroValForType(tp *types.FieldType) interface{} {
	switch tp.Tp {
	case mysql.TypeFloat:
		return float32(0)
	case mysql.TypeDouble:
		return float64(0)
	case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear:
		if mysql.HasUnsignedFlag(tp.Flag) {
			return uint64(0)
		}
		return int64(0)
	case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar:
		return ""
	case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
		return []byte{}
	case mysql.TypeDuration:
		return types.ZeroDuration
	case mysql.TypeNewDecimal:
		return types.NewDecFromInt(0)
	case mysql.TypeDate:
		return types.ZeroDate
	case mysql.TypeDatetime:
		return types.ZeroDatetime
	case mysql.TypeTimestamp:
		return types.ZeroTimestamp
	case mysql.TypeBit:
		return types.BinaryLiteral{}
	case mysql.TypeSet:
		return types.Set{}
	case mysql.TypeEnum:
		return types.Enum{}
	case mysql.TypeJSON:
		return json.CreateBinary(nil)
	default:
		return nil
	}
}

func makeMutRowColumn(in interface{}) *column {
	switch x := in.(type) {
	case nil:
		col := makeMutRowUint64Column(uint64(0))
		col.nullBitmap[0] = 0
		return col
	case int:
		return makeMutRowUint64Column(uint64(x))
	case int64:
		return makeMutRowUint64Column(uint64(x))
	case uint64:
		return makeMutRowUint64Column(x)
	case float64:
		return makeMutRowUint64Column(math.Float64bits(x))
	case float32:
		col := newMutRowFixedLenColumn(4)
		*(*uint32)(unsafe.Pointer(&col.data[0])) = math.Float32bits(x)
		return col
	case string:
		return makeMutRowBytesColumn(hack.Slice(x))
	case []byte:
		return makeMutRowBytesColumn(x)
	case types.BinaryLiteral:
		return makeMutRowBytesColumn(x)
	case *types.MyDecimal:
		col := newMutRowFixedLenColumn(types.MyDecimalStructSize)
		*(*types.MyDecimal)(unsafe.Pointer(&col.data[0])) = *x
		return col
	case types.Time:
		col := newMutRowFixedLenColumn(16)
		writeTime(col.data, x)
		return col
	case json.BinaryJSON:
		col := newMutRowVarLenColumn(len(x.Value) + 1)
		col.data[0] = x.TypeCode
		copy(col.data[1:], x.Value)
		return col
	case types.Duration:
		col := newMutRowFixedLenColumn(16)
		*(*types.Duration)(unsafe.Pointer(&col.data[0])) = x
		return col
	case types.Enum:
		col := newMutRowVarLenColumn(len(x.Name) + 8)
		*(*uint64)(unsafe.Pointer(&col.data[0])) = x.Value
		copy(col.data[8:], x.Name)
		return col
	case types.Set:
		col := newMutRowVarLenColumn(len(x.Name) + 8)
		*(*uint64)(unsafe.Pointer(&col.data[0])) = x.Value
		copy(col.data[8:], x.Name)
		return col
	default:
		return nil
	}
}

func newMutRowFixedLenColumn(elemSize int) *column {
	buf := make([]byte, elemSize+1)
	col := &column{
		length:     1,
		elemBuf:    buf[:elemSize],
		data:       buf[:elemSize],
		nullBitmap: buf[elemSize:],
	}
	col.nullBitmap[0] = 1
	return col
}

func newMutRowVarLenColumn(valSize int) *column {
	buf := make([]byte, valSize+1)
	col := &column{
		length:     1,
		offsets:    []int32{0, int32(valSize)},
		data:       buf[:valSize],
		nullBitmap: buf[valSize:],
	}
	col.nullBitmap[0] = 1
	return col
}

func makeMutRowUint64Column(val uint64) *column {
	col := newMutRowFixedLenColumn(8)
	*(*uint64)(unsafe.Pointer(&col.data[0])) = val
	return col
}

func makeMutRowBytesColumn(bin []byte) *column {
	col := newMutRowVarLenColumn(len(bin))
	copy(col.data, bin)
	col.nullBitmap[0] = 1
	return col
}

// SetRow sets the MutRow with Row.
func (mr MutRow) SetRow(row Row) {
	for colIdx, rCol := range row.c.columns {
		mrCol := mr.c.columns[colIdx]
		if rCol.isNull(row.idx) {
			mrCol.nullBitmap[0] = 0
			continue
		}
		elemLen := len(rCol.elemBuf)
		if elemLen > 0 {
			copy(mrCol.data, rCol.data[row.idx*elemLen:(row.idx+1)*elemLen])
		} else {
			setMutRowBytes(mrCol, rCol.data[rCol.offsets[row.idx]:rCol.offsets[row.idx+1]])
		}
		mrCol.nullBitmap[0] = 1
	}
}

// SetValues sets the MutRow with values.
func (mr MutRow) SetValues(vals ...interface{}) {
	for i, v := range vals {
		mr.SetValue(i, v)
	}
}

// SetValue sets the MutRow with colIdx and value.
func (mr MutRow) SetValue(colIdx int, val interface{}) {
	col := mr.c.columns[colIdx]
	if val == nil {
		col.nullBitmap[0] = 0
		return
	}
	switch x := val.(type) {
	case int:
		binary.LittleEndian.PutUint64(col.data, uint64(x))
	case int64:
		binary.LittleEndian.PutUint64(col.data, uint64(x))
	case uint64:
		binary.LittleEndian.PutUint64(col.data, x)
	case float64:
		binary.LittleEndian.PutUint64(col.data, math.Float64bits(x))
	case float32:
		binary.LittleEndian.PutUint32(col.data, math.Float32bits(x))
	case string:
		setMutRowBytes(col, hack.Slice(x))
	case []byte:
		setMutRowBytes(col, x)
	case types.BinaryLiteral:
		setMutRowBytes(col, x)
	case types.Duration:
		*(*types.Duration)(unsafe.Pointer(&col.data[0])) = x
	case *types.MyDecimal:
		*(*types.MyDecimal)(unsafe.Pointer(&col.data[0])) = *x
	case types.Time:
		writeTime(col.data, x)
	case types.Enum:
		setMutRowNameValue(col, x.Name, x.Value)
	case types.Set:
		setMutRowNameValue(col, x.Name, x.Value)
	case json.BinaryJSON:
		setMutRowJSON(col, x)
	}
	col.nullBitmap[0] = 1
}

// SetDatums sets the MutRow with datum slice.
func (mr MutRow) SetDatums(datums ...types.Datum) {
	for i, d := range datums {
		mr.SetDatum(i, d)
	}
}

// SetDatum sets the MutRow with colIdx and datum.
func (mr MutRow) SetDatum(colIdx int, d types.Datum) {
	col := mr.c.columns[colIdx]
	if d.IsNull() {
		col.nullBitmap[0] = 0
		return
	}
	switch d.Kind() {
	case types.KindInt64, types.KindUint64, types.KindFloat64:
		binary.LittleEndian.PutUint64(mr.c.columns[colIdx].data, d.GetUint64())
	case types.KindFloat32:
		binary.LittleEndian.PutUint32(mr.c.columns[colIdx].data, math.Float32bits(d.GetFloat32()))
	case types.KindString, types.KindBytes, types.KindBinaryLiteral:
		setMutRowBytes(col, d.GetBytes())
	case types.KindMysqlTime:
		writeTime(col.data, d.GetMysqlTime())
	case types.KindMysqlDuration:
		*(*types.Duration)(unsafe.Pointer(&col.data[0])) = d.GetMysqlDuration()
	case types.KindMysqlDecimal:
		*(*types.MyDecimal)(unsafe.Pointer(&col.data[0])) = *d.GetMysqlDecimal()
	case types.KindMysqlJSON:
		setMutRowJSON(col, d.GetMysqlJSON())
	case types.KindMysqlEnum:
		e := d.GetMysqlEnum()
		setMutRowNameValue(col, e.Name, e.Value)
	case types.KindMysqlSet:
		s := d.GetMysqlSet()
		setMutRowNameValue(col, s.Name, s.Value)
	default:
		mr.c.columns[colIdx] = makeMutRowColumn(d.GetValue())
	}
	col.nullBitmap[0] = 1
}

func setMutRowBytes(col *column, bin []byte) {
	if len(col.data) >= len(bin) {
		col.data = col.data[:len(bin)]
	} else {
		buf := make([]byte, len(bin)+1)
		col.data = buf[:len(bin)]
		col.nullBitmap = buf[len(bin):]
	}
	copy(col.data, bin)
	col.offsets[1] = int32(len(bin))
}

func setMutRowNameValue(col *column, name string, val uint64) {
	dataLen := len(name) + 8
	if len(col.data) >= dataLen {
		col.data = col.data[:dataLen]
	} else {
		buf := make([]byte, dataLen+1)
		col.data = buf[:dataLen]
		col.nullBitmap = buf[dataLen:]
	}
	binary.LittleEndian.PutUint64(col.data, val)
	copy(col.data[8:], name)
	col.offsets[1] = int32(dataLen)
}

func setMutRowJSON(col *column, j json.BinaryJSON) {
	dataLen := len(j.Value) + 1
	if len(col.data) >= dataLen {
		col.data = col.data[:dataLen]
	} else {
		// In MutRow, there always exists 1 data in every column,
		// we should allocate one more byte for null bitmap.
		buf := make([]byte, dataLen+1)
		col.data = buf[:dataLen]
		col.nullBitmap = buf[dataLen:]
	}
	col.data[0] = j.TypeCode
	copy(col.data[1:], j.Value)
	col.offsets[1] = int32(dataLen)
}

// ShallowCopyPartialRow shallow copies the data of `row` to MutRow.
func (mr MutRow) ShallowCopyPartialRow(colIdx int, row Row) {
	for i, srcCol := range row.c.columns {
		dstCol := mr.c.columns[colIdx+i]
		if !srcCol.isNull(row.idx) {
			// MutRow only contains one row, so we can directly set the whole byte.
			dstCol.nullBitmap[0] = 1
		} else {
			dstCol.nullBitmap[0] = 0
		}

		if srcCol.isFixed() {
			elemLen := len(srcCol.elemBuf)
			offset := row.idx * elemLen
			dstCol.data = srcCol.data[offset : offset+elemLen]
		} else {
			start, end := srcCol.offsets[row.idx], srcCol.offsets[row.idx+1]
			dstCol.data = srcCol.data[start:end]
			dstCol.offsets[1] = int32(len(dstCol.data))
		}
	}
}
<commit_msg>util/chunk: preallocate the columns array in MutRow to avoid array resize (#8348)<commit_after>// Copyright 2017 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 chunk

import (
	"encoding/binary"
	"math"
	"unsafe"

	"github.com/pingcap/parser/mysql"
	"github.com/pingcap/tidb/types"
	"github.com/pingcap/tidb/types/json"
	"github.com/pingcap/tidb/util/hack"
)

// MutRow represents a mutable Row.
// The underlying columns only contains one row and not exposed to the user.
type MutRow Row

// ToRow converts the MutRow to Row, so it can be used to read data.
func (mr MutRow) ToRow() Row {
	return Row(mr)
}

// Len returns the number of columns.
func (mr MutRow) Len() int {
	return len(mr.c.columns)
}

// MutRowFromValues creates a MutRow from a interface slice.
func MutRowFromValues(vals ...interface{}) MutRow {
	c := &Chunk{columns: make([]*column, 0, len(vals))}
	for _, val := range vals {
		col := makeMutRowColumn(val)
		c.columns = append(c.columns, col)
	}
	return MutRow{c: c}
}

// MutRowFromDatums creates a MutRow from a datum slice.
func MutRowFromDatums(datums []types.Datum) MutRow {
	c := &Chunk{columns: make([]*column, 0, len(datums))}
	for _, d := range datums {
		col := makeMutRowColumn(d.GetValue())
		c.columns = append(c.columns, col)
	}
	return MutRow{c: c, idx: 0}
}

// MutRowFromTypes creates a MutRow from a FieldType slice, each column is initialized to zero value.
func MutRowFromTypes(types []*types.FieldType) MutRow {
	c := &Chunk{columns: make([]*column, 0, len(types))}
	for _, tp := range types {
		col := makeMutRowColumn(zeroValForType(tp))
		c.columns = append(c.columns, col)
	}
	return MutRow{c: c, idx: 0}
}

func zeroValForType(tp *types.FieldType) interface{} {
	switch tp.Tp {
	case mysql.TypeFloat:
		return float32(0)
	case mysql.TypeDouble:
		return float64(0)
	case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear:
		if mysql.HasUnsignedFlag(tp.Flag) {
			return uint64(0)
		}
		return int64(0)
	case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar:
		return ""
	case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
		return []byte{}
	case mysql.TypeDuration:
		return types.ZeroDuration
	case mysql.TypeNewDecimal:
		return types.NewDecFromInt(0)
	case mysql.TypeDate:
		return types.ZeroDate
	case mysql.TypeDatetime:
		return types.ZeroDatetime
	case mysql.TypeTimestamp:
		return types.ZeroTimestamp
	case mysql.TypeBit:
		return types.BinaryLiteral{}
	case mysql.TypeSet:
		return types.Set{}
	case mysql.TypeEnum:
		return types.Enum{}
	case mysql.TypeJSON:
		return json.CreateBinary(nil)
	default:
		return nil
	}
}

func makeMutRowColumn(in interface{}) *column {
	switch x := in.(type) {
	case nil:
		col := makeMutRowUint64Column(uint64(0))
		col.nullBitmap[0] = 0
		return col
	case int:
		return makeMutRowUint64Column(uint64(x))
	case int64:
		return makeMutRowUint64Column(uint64(x))
	case uint64:
		return makeMutRowUint64Column(x)
	case float64:
		return makeMutRowUint64Column(math.Float64bits(x))
	case float32:
		col := newMutRowFixedLenColumn(4)
		*(*uint32)(unsafe.Pointer(&col.data[0])) = math.Float32bits(x)
		return col
	case string:
		return makeMutRowBytesColumn(hack.Slice(x))
	case []byte:
		return makeMutRowBytesColumn(x)
	case types.BinaryLiteral:
		return makeMutRowBytesColumn(x)
	case *types.MyDecimal:
		col := newMutRowFixedLenColumn(types.MyDecimalStructSize)
		*(*types.MyDecimal)(unsafe.Pointer(&col.data[0])) = *x
		return col
	case types.Time:
		col := newMutRowFixedLenColumn(16)
		writeTime(col.data, x)
		return col
	case json.BinaryJSON:
		col := newMutRowVarLenColumn(len(x.Value) + 1)
		col.data[0] = x.TypeCode
		copy(col.data[1:], x.Value)
		return col
	case types.Duration:
		col := newMutRowFixedLenColumn(16)
		*(*types.Duration)(unsafe.Pointer(&col.data[0])) = x
		return col
	case types.Enum:
		col := newMutRowVarLenColumn(len(x.Name) + 8)
		*(*uint64)(unsafe.Pointer(&col.data[0])) = x.Value
		copy(col.data[8:], x.Name)
		return col
	case types.Set:
		col := newMutRowVarLenColumn(len(x.Name) + 8)
		*(*uint64)(unsafe.Pointer(&col.data[0])) = x.Value
		copy(col.data[8:], x.Name)
		return col
	default:
		return nil
	}
}

func newMutRowFixedLenColumn(elemSize int) *column {
	buf := make([]byte, elemSize+1)
	col := &column{
		length:     1,
		elemBuf:    buf[:elemSize],
		data:       buf[:elemSize],
		nullBitmap: buf[elemSize:],
	}
	col.nullBitmap[0] = 1
	return col
}

func newMutRowVarLenColumn(valSize int) *column {
	buf := make([]byte, valSize+1)
	col := &column{
		length:     1,
		offsets:    []int32{0, int32(valSize)},
		data:       buf[:valSize],
		nullBitmap: buf[valSize:],
	}
	col.nullBitmap[0] = 1
	return col
}

func makeMutRowUint64Column(val uint64) *column {
	col := newMutRowFixedLenColumn(8)
	*(*uint64)(unsafe.Pointer(&col.data[0])) = val
	return col
}

func makeMutRowBytesColumn(bin []byte) *column {
	col := newMutRowVarLenColumn(len(bin))
	copy(col.data, bin)
	col.nullBitmap[0] = 1
	return col
}

// SetRow sets the MutRow with Row.
func (mr MutRow) SetRow(row Row) {
	for colIdx, rCol := range row.c.columns {
		mrCol := mr.c.columns[colIdx]
		if rCol.isNull(row.idx) {
			mrCol.nullBitmap[0] = 0
			continue
		}
		elemLen := len(rCol.elemBuf)
		if elemLen > 0 {
			copy(mrCol.data, rCol.data[row.idx*elemLen:(row.idx+1)*elemLen])
		} else {
			setMutRowBytes(mrCol, rCol.data[rCol.offsets[row.idx]:rCol.offsets[row.idx+1]])
		}
		mrCol.nullBitmap[0] = 1
	}
}

// SetValues sets the MutRow with values.
func (mr MutRow) SetValues(vals ...interface{}) {
	for i, v := range vals {
		mr.SetValue(i, v)
	}
}

// SetValue sets the MutRow with colIdx and value.
func (mr MutRow) SetValue(colIdx int, val interface{}) {
	col := mr.c.columns[colIdx]
	if val == nil {
		col.nullBitmap[0] = 0
		return
	}
	switch x := val.(type) {
	case int:
		binary.LittleEndian.PutUint64(col.data, uint64(x))
	case int64:
		binary.LittleEndian.PutUint64(col.data, uint64(x))
	case uint64:
		binary.LittleEndian.PutUint64(col.data, x)
	case float64:
		binary.LittleEndian.PutUint64(col.data, math.Float64bits(x))
	case float32:
		binary.LittleEndian.PutUint32(col.data, math.Float32bits(x))
	case string:
		setMutRowBytes(col, hack.Slice(x))
	case []byte:
		setMutRowBytes(col, x)
	case types.BinaryLiteral:
		setMutRowBytes(col, x)
	case types.Duration:
		*(*types.Duration)(unsafe.Pointer(&col.data[0])) = x
	case *types.MyDecimal:
		*(*types.MyDecimal)(unsafe.Pointer(&col.data[0])) = *x
	case types.Time:
		writeTime(col.data, x)
	case types.Enum:
		setMutRowNameValue(col, x.Name, x.Value)
	case types.Set:
		setMutRowNameValue(col, x.Name, x.Value)
	case json.BinaryJSON:
		setMutRowJSON(col, x)
	}
	col.nullBitmap[0] = 1
}

// SetDatums sets the MutRow with datum slice.
func (mr MutRow) SetDatums(datums ...types.Datum) {
	for i, d := range datums {
		mr.SetDatum(i, d)
	}
}

// SetDatum sets the MutRow with colIdx and datum.
func (mr MutRow) SetDatum(colIdx int, d types.Datum) {
	col := mr.c.columns[colIdx]
	if d.IsNull() {
		col.nullBitmap[0] = 0
		return
	}
	switch d.Kind() {
	case types.KindInt64, types.KindUint64, types.KindFloat64:
		binary.LittleEndian.PutUint64(mr.c.columns[colIdx].data, d.GetUint64())
	case types.KindFloat32:
		binary.LittleEndian.PutUint32(mr.c.columns[colIdx].data, math.Float32bits(d.GetFloat32()))
	case types.KindString, types.KindBytes, types.KindBinaryLiteral:
		setMutRowBytes(col, d.GetBytes())
	case types.KindMysqlTime:
		writeTime(col.data, d.GetMysqlTime())
	case types.KindMysqlDuration:
		*(*types.Duration)(unsafe.Pointer(&col.data[0])) = d.GetMysqlDuration()
	case types.KindMysqlDecimal:
		*(*types.MyDecimal)(unsafe.Pointer(&col.data[0])) = *d.GetMysqlDecimal()
	case types.KindMysqlJSON:
		setMutRowJSON(col, d.GetMysqlJSON())
	case types.KindMysqlEnum:
		e := d.GetMysqlEnum()
		setMutRowNameValue(col, e.Name, e.Value)
	case types.KindMysqlSet:
		s := d.GetMysqlSet()
		setMutRowNameValue(col, s.Name, s.Value)
	default:
		mr.c.columns[colIdx] = makeMutRowColumn(d.GetValue())
	}
	col.nullBitmap[0] = 1
}

func setMutRowBytes(col *column, bin []byte) {
	if len(col.data) >= len(bin) {
		col.data = col.data[:len(bin)]
	} else {
		buf := make([]byte, len(bin)+1)
		col.data = buf[:len(bin)]
		col.nullBitmap = buf[len(bin):]
	}
	copy(col.data, bin)
	col.offsets[1] = int32(len(bin))
}

func setMutRowNameValue(col *column, name string, val uint64) {
	dataLen := len(name) + 8
	if len(col.data) >= dataLen {
		col.data = col.data[:dataLen]
	} else {
		buf := make([]byte, dataLen+1)
		col.data = buf[:dataLen]
		col.nullBitmap = buf[dataLen:]
	}
	binary.LittleEndian.PutUint64(col.data, val)
	copy(col.data[8:], name)
	col.offsets[1] = int32(dataLen)
}

func setMutRowJSON(col *column, j json.BinaryJSON) {
	dataLen := len(j.Value) + 1
	if len(col.data) >= dataLen {
		col.data = col.data[:dataLen]
	} else {
		// In MutRow, there always exists 1 data in every column,
		// we should allocate one more byte for null bitmap.
		buf := make([]byte, dataLen+1)
		col.data = buf[:dataLen]
		col.nullBitmap = buf[dataLen:]
	}
	col.data[0] = j.TypeCode
	copy(col.data[1:], j.Value)
	col.offsets[1] = int32(dataLen)
}

// ShallowCopyPartialRow shallow copies the data of `row` to MutRow.
func (mr MutRow) ShallowCopyPartialRow(colIdx int, row Row) {
	for i, srcCol := range row.c.columns {
		dstCol := mr.c.columns[colIdx+i]
		if !srcCol.isNull(row.idx) {
			// MutRow only contains one row, so we can directly set the whole byte.
			dstCol.nullBitmap[0] = 1
		} else {
			dstCol.nullBitmap[0] = 0
		}

		if srcCol.isFixed() {
			elemLen := len(srcCol.elemBuf)
			offset := row.idx * elemLen
			dstCol.data = srcCol.data[offset : offset+elemLen]
		} else {
			start, end := srcCol.offsets[row.idx], srcCol.offsets[row.idx+1]
			dstCol.data = srcCol.data[start:end]
			dstCol.offsets[1] = int32(len(dstCol.data))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package logging

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/ava-labs/avalanchego/utils/perms"
)

var (
	fileSuffix = "utils/logging/log.go"
	filePrefix string
)

func init() {
	_, file, _, ok := runtime.Caller(0)
	if !ok {
		panic("failed to get file")
	}
	if len(file) < len(fileSuffix) {
		panic("unexpected file name returned")
	}
	filePrefix = file[:len(file)-len(fileSuffix)]
}

// Log implements the Logger interface
type Log struct {
	config Config

	messages []string
	size     int

	wg                               sync.WaitGroup
	flushLock, writeLock, configLock sync.Mutex
	needsFlush                       *sync.Cond

	closed bool

	writer RotatingWriter
}

// New returns a new logger set up according to [config]
func newLog(config Config) (*Log, error) {
	if err := os.MkdirAll(config.Directory, perms.ReadWriteExecute); err != nil {
		return nil, err
	}
	l := &Log{
		config: config,
		writer: &fileWriter{},
	}
	l.needsFlush = sync.NewCond(&l.flushLock)

	l.wg.Add(1)

	go l.RecoverAndPanic(l.run)

	return l, nil
}

func (l *Log) run() {
	defer l.wg.Done()

	l.writeLock.Lock()
	defer l.writeLock.Unlock()

	currentSize, err := l.writer.Initialize(l.config)
	if err != nil {
		panic(err)
	}

	closed := false
	nextRotation := time.Now().Add(l.config.RotationInterval)

	for !closed {
		l.writeLock.Unlock()
		l.flushLock.Lock()
		for l.size < l.config.FlushSize && !l.closed {
			l.needsFlush.Wait()
		}
		closed = l.closed
		prevMessages := l.messages
		l.messages = nil
		l.size = 0
		l.flushLock.Unlock()
		l.writeLock.Lock()

		for _, msg := range prevMessages {
			n, _ := l.writer.WriteString(msg)
			currentSize += n
		}

		if !l.config.DisableFlushOnWrite {
			// attempt to flush after the write
			_ = l.writer.Flush()
		}

		if now := time.Now(); nextRotation.Before(now) || currentSize > l.config.FileSize {
			nextRotation = now.Add(l.config.RotationInterval)
			currentSize = 0
			// attempt to flush before closing
			_ = l.writer.Flush()
			// attempt to close the file
			_ = l.writer.Close()

			if err := l.writer.Rotate(); err != nil {
				panic(err)
			}
		}
	}
	// attempt to flush when exiting
	_ = l.writer.Flush()
	// attempt to close the file when exiting
	_ = l.writer.Close()
}

func (l *Log) Write(p []byte) (int, error) {
	if l == nil {
		return 0, nil
	}

	l.configLock.Lock()
	defer l.configLock.Unlock()

	if !l.config.DisableLogging {
		l.flushLock.Lock()
		l.messages = append(l.messages, string(p))
		l.size += len(p)
		l.needsFlush.Signal()
		l.flushLock.Unlock()
	}

	return len(p), nil
}

// Stop implements the Logger interface
func (l *Log) Stop() {
	l.flushLock.Lock()
	l.closed = true
	l.needsFlush.Signal()
	l.flushLock.Unlock()

	l.wg.Wait()
}

// Should only be called from [Level] functions.
func (l *Log) log(level Level, format string, args ...interface{}) {
	if l == nil {
		return
	}

	l.configLock.Lock()
	defer l.configLock.Unlock()

	shouldLog := !l.config.DisableLogging && level <= l.config.LogLevel
	shouldDisplay := (!l.config.DisableDisplaying && level <= l.config.DisplayLevel) || level == Fatal

	if !shouldLog && !shouldDisplay {
		return
	}

	args = SanitizeArgs(args)

	output := l.format(level, format, args...)

	if shouldLog {
		l.flushLock.Lock()
		l.messages = append(l.messages, output)
		l.size += len(output)
		l.needsFlush.Signal()
		l.flushLock.Unlock()
	}

	if shouldDisplay {
		switch {
		case l.config.DisableContextualDisplaying:
			fmt.Println(fmt.Sprintf(format, args...))
		case l.config.DisplayHighlight == Plain:
			fmt.Print(output)
		default:
			fmt.Print(level.Color().Wrap(output))
		}
	}
}

func (l *Log) format(level Level, format string, args ...interface{}) string {
	loc := "?"
	if _, file, no, ok := runtime.Caller(3); ok {
		localFile := strings.TrimPrefix(file, filePrefix)
		loc = fmt.Sprintf("%s#%d", localFile, no)
	}
	text := fmt.Sprintf("%s: %s", loc, fmt.Sprintf(format, args...))

	prefix := ""
	if l.config.MsgPrefix != "" {
		prefix = fmt.Sprintf(" <%s>", l.config.MsgPrefix)
	}

	return fmt.Sprintf("%s[%s]%s %s\n",
		level.AlignedString(),
		time.Now().Format("01-02|15:04:05"),
		prefix,
		text)
}

// Fatal implements the Logger interface
func (l *Log) Fatal(format string, args ...interface{}) { l.log(Fatal, format, args...) }

// Error implements the Logger interface
func (l *Log) Error(format string, args ...interface{}) { l.log(Error, format, args...) }

// Warn implements the Logger interface
func (l *Log) Warn(format string, args ...interface{}) { l.log(Warn, format, args...) }

// Info implements the Logger interface
func (l *Log) Info(format string, args ...interface{}) { l.log(Info, format, args...) }

// Trace implements the Logger interface
func (l *Log) Trace(format string, args ...interface{}) { l.log(Trace, format, args...) }

// Debug implements the Logger interface
func (l *Log) Debug(format string, args ...interface{}) { l.log(Debug, format, args...) }

// Verbo implements the Logger interface
func (l *Log) Verbo(format string, args ...interface{}) { l.log(Verbo, format, args...) }

// AssertNoError implements the Logger interface
func (l *Log) AssertNoError(err error) {
	if err != nil {
		l.log(Fatal, "%s", err)
	}
	if l.config.Assertions && err != nil {
		l.Stop()
		panic(err)
	}
}

// AssertTrue implements the Logger interface
func (l *Log) AssertTrue(b bool, format string, args ...interface{}) {
	if !b {
		l.log(Fatal, format, args...)
	}
	if l.config.Assertions && !b {
		l.Stop()
		panic(fmt.Sprintf(format, args...))
	}
}

// AssertDeferredTrue implements the Logger interface
func (l *Log) AssertDeferredTrue(f func() bool, format string, args ...interface{}) {
	// Note, the logger will only be notified here if assertions are enabled
	if l.config.Assertions && !f() {
		err := fmt.Sprintf(format, args...)
		l.log(Fatal, err)
		l.Stop()
		panic(err)
	}
}

// AssertDeferredNoError implements the Logger interface
func (l *Log) AssertDeferredNoError(f func() error) {
	if l.config.Assertions {
		err := f()
		if err != nil {
			l.log(Fatal, "%s", err)
		}
		if l.config.Assertions && err != nil {
			l.Stop()
			panic(err)
		}
	}
}

// StopOnPanic implements the Logger interface
func (l *Log) StopOnPanic() {
	if r := recover(); r != nil {
		l.Fatal("Panicking due to:\n%s\nFrom:\n%s", r, Stacktrace{})
		l.Stop()
		panic(r)
	}
}

// RecoverAndPanic implements the Logger interface
func (l *Log) RecoverAndPanic(f func()) { defer l.StopOnPanic(); f() }

func (l *Log) stopAndExit(exit func()) {
	if r := recover(); r != nil {
		l.Fatal("Panicking due to:\n%s\nFrom:\n%s", r, Stacktrace{})
		l.Stop()
		exit()
	}
}

// RecoverAndExit implements the Logger interface
func (l *Log) RecoverAndExit(f, exit func()) { defer l.stopAndExit(exit); f() }

func (l *Log) SetLogLevel(lvl Level) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.LogLevel = lvl
}

// GetLogLevel ...
func (l *Log) GetLogLevel() Level {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	return l.config.LogLevel
}

// GetDisplayLevel implements the Logger interface
func (l *Log) GetDisplayLevel() Level {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	return l.config.DisplayLevel
}

// SetDisplayLevel implements the Logger interface
func (l *Log) SetDisplayLevel(lvl Level) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisplayLevel = lvl
}

// SetPrefix implements the Logger interface
func (l *Log) SetPrefix(prefix string) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.MsgPrefix = prefix
}

// SetLoggingEnabled implements the Logger interface
func (l *Log) SetLoggingEnabled(enabled bool) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisableLogging = !enabled
}

// SetDisplayingEnabled implements the Logger interface
func (l *Log) SetDisplayingEnabled(enabled bool) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisableDisplaying = !enabled
}

// SetContextualDisplayingEnabled implements the Logger interface
func (l *Log) SetContextualDisplayingEnabled(enabled bool) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisableContextualDisplaying = !enabled
}

// fileWriter implements the RotatingWriter interface
type fileWriter struct {
	writer *bufio.Writer
	file   *os.File

	config Config
}

// Flush implements the RotatingWriter interface
func (fw *fileWriter) Flush() error {
	return fw.writer.Flush()
}

// Write implements the RotatingWriter interface
func (fw *fileWriter) Write(b []byte) (int, error) {
	return fw.writer.Write(b)
}

// WriteString implements the RotatingWriter interface
func (fw *fileWriter) WriteString(s string) (int, error) {
	return fw.writer.WriteString(s)
}

// Close implements the RotatingWriter interface
func (fw *fileWriter) Close() error {
	return fw.file.Close()
}

// Rotate implements the RotatingWriter interface
func (fw *fileWriter) Rotate() error {
	for i := fw.config.RotationSize - 1; i > 0; i-- {
		sourceFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log.%d", fw.config.LoggerName, i))
		destFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log.%d", fw.config.LoggerName, i+1))
		if _, err := os.Stat(sourceFilename); !errors.Is(err, os.ErrNotExist) {
			if err := os.Rename(sourceFilename, destFilename); err != nil {
				return err
			}
		}
	}
	sourceFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log", fw.config.LoggerName))
	destFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log.1", fw.config.LoggerName))
	if err := os.Rename(sourceFilename, destFilename); err != nil {
		return err
	}
	writer, file, err := fw.create()
	if err != nil {
		return err
	}
	fw.file = file
	fw.writer = writer
	return nil
}

// Creates a file if it does not exist or opens it in append mode if it does
func (fw *fileWriter) create() (*bufio.Writer, *os.File, error) {
	filename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log", fw.config.LoggerName))
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, perms.ReadWrite)
	if err != nil {
		return nil, nil, err
	}
	writer := bufio.NewWriter(file)
	return writer, file, nil
}

// Initialize implements the RotatingWriter interface
func (fw *fileWriter) Initialize(config Config) (int, error) {
	fw.config = config
	writer, file, err := fw.create()
	if err != nil {
		return 0, err
	}
	fw.writer = writer
	fw.file = file
	fileinfo, err := file.Stat()
	if err != nil {
		return 0, err
	}
	fileSize := fileinfo.Size()
	return int(fileSize), nil
}
<commit_msg>Update log time format to include ms (#1155)<commit_after>// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package logging

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/ava-labs/avalanchego/utils/perms"
)

var (
	fileSuffix = "utils/logging/log.go"
	filePrefix string
	timeFormat = "01-02|15:04:05.000"
)

func init() {
	_, file, _, ok := runtime.Caller(0)
	if !ok {
		panic("failed to get file")
	}
	if len(file) < len(fileSuffix) {
		panic("unexpected file name returned")
	}
	filePrefix = file[:len(file)-len(fileSuffix)]
}

// Log implements the Logger interface
type Log struct {
	config Config

	messages []string
	size     int

	wg                               sync.WaitGroup
	flushLock, writeLock, configLock sync.Mutex
	needsFlush                       *sync.Cond

	closed bool

	writer RotatingWriter
}

// New returns a new logger set up according to [config]
func newLog(config Config) (*Log, error) {
	if err := os.MkdirAll(config.Directory, perms.ReadWriteExecute); err != nil {
		return nil, err
	}
	l := &Log{
		config: config,
		writer: &fileWriter{},
	}
	l.needsFlush = sync.NewCond(&l.flushLock)

	l.wg.Add(1)

	go l.RecoverAndPanic(l.run)

	return l, nil
}

func (l *Log) run() {
	defer l.wg.Done()

	l.writeLock.Lock()
	defer l.writeLock.Unlock()

	currentSize, err := l.writer.Initialize(l.config)
	if err != nil {
		panic(err)
	}

	closed := false
	nextRotation := time.Now().Add(l.config.RotationInterval)

	for !closed {
		l.writeLock.Unlock()
		l.flushLock.Lock()
		for l.size < l.config.FlushSize && !l.closed {
			l.needsFlush.Wait()
		}
		closed = l.closed
		prevMessages := l.messages
		l.messages = nil
		l.size = 0
		l.flushLock.Unlock()
		l.writeLock.Lock()

		for _, msg := range prevMessages {
			n, _ := l.writer.WriteString(msg)
			currentSize += n
		}

		if !l.config.DisableFlushOnWrite {
			// attempt to flush after the write
			_ = l.writer.Flush()
		}

		if now := time.Now(); nextRotation.Before(now) || currentSize > l.config.FileSize {
			nextRotation = now.Add(l.config.RotationInterval)
			currentSize = 0
			// attempt to flush before closing
			_ = l.writer.Flush()
			// attempt to close the file
			_ = l.writer.Close()

			if err := l.writer.Rotate(); err != nil {
				panic(err)
			}
		}
	}
	// attempt to flush when exiting
	_ = l.writer.Flush()
	// attempt to close the file when exiting
	_ = l.writer.Close()
}

func (l *Log) Write(p []byte) (int, error) {
	if l == nil {
		return 0, nil
	}

	l.configLock.Lock()
	defer l.configLock.Unlock()

	if !l.config.DisableLogging {
		l.flushLock.Lock()
		l.messages = append(l.messages, string(p))
		l.size += len(p)
		l.needsFlush.Signal()
		l.flushLock.Unlock()
	}

	return len(p), nil
}

// Stop implements the Logger interface
func (l *Log) Stop() {
	l.flushLock.Lock()
	l.closed = true
	l.needsFlush.Signal()
	l.flushLock.Unlock()

	l.wg.Wait()
}

// Should only be called from [Level] functions.
func (l *Log) log(level Level, format string, args ...interface{}) {
	if l == nil {
		return
	}

	l.configLock.Lock()
	defer l.configLock.Unlock()

	shouldLog := !l.config.DisableLogging && level <= l.config.LogLevel
	shouldDisplay := (!l.config.DisableDisplaying && level <= l.config.DisplayLevel) || level == Fatal

	if !shouldLog && !shouldDisplay {
		return
	}

	args = SanitizeArgs(args)

	output := l.format(level, format, args...)

	if shouldLog {
		l.flushLock.Lock()
		l.messages = append(l.messages, output)
		l.size += len(output)
		l.needsFlush.Signal()
		l.flushLock.Unlock()
	}

	if shouldDisplay {
		switch {
		case l.config.DisableContextualDisplaying:
			fmt.Println(fmt.Sprintf(format, args...))
		case l.config.DisplayHighlight == Plain:
			fmt.Print(output)
		default:
			fmt.Print(level.Color().Wrap(output))
		}
	}
}

func (l *Log) format(level Level, format string, args ...interface{}) string {
	loc := "?"
	if _, file, no, ok := runtime.Caller(3); ok {
		localFile := strings.TrimPrefix(file, filePrefix)
		loc = fmt.Sprintf("%s#%d", localFile, no)
	}
	text := fmt.Sprintf("%s: %s", loc, fmt.Sprintf(format, args...))

	prefix := ""
	if l.config.MsgPrefix != "" {
		prefix = fmt.Sprintf(" <%s>", l.config.MsgPrefix)
	}

	return fmt.Sprintf("%s[%s]%s %s\n",
		level.AlignedString(),
		time.Now().Format(timeFormat),
		prefix,
		text)
}

// Fatal implements the Logger interface
func (l *Log) Fatal(format string, args ...interface{}) { l.log(Fatal, format, args...) }

// Error implements the Logger interface
func (l *Log) Error(format string, args ...interface{}) { l.log(Error, format, args...) }

// Warn implements the Logger interface
func (l *Log) Warn(format string, args ...interface{}) { l.log(Warn, format, args...) }

// Info implements the Logger interface
func (l *Log) Info(format string, args ...interface{}) { l.log(Info, format, args...) }

// Trace implements the Logger interface
func (l *Log) Trace(format string, args ...interface{}) { l.log(Trace, format, args...) }

// Debug implements the Logger interface
func (l *Log) Debug(format string, args ...interface{}) { l.log(Debug, format, args...) }

// Verbo implements the Logger interface
func (l *Log) Verbo(format string, args ...interface{}) { l.log(Verbo, format, args...) }

// AssertNoError implements the Logger interface
func (l *Log) AssertNoError(err error) {
	if err != nil {
		l.log(Fatal, "%s", err)
	}
	if l.config.Assertions && err != nil {
		l.Stop()
		panic(err)
	}
}

// AssertTrue implements the Logger interface
func (l *Log) AssertTrue(b bool, format string, args ...interface{}) {
	if !b {
		l.log(Fatal, format, args...)
	}
	if l.config.Assertions && !b {
		l.Stop()
		panic(fmt.Sprintf(format, args...))
	}
}

// AssertDeferredTrue implements the Logger interface
func (l *Log) AssertDeferredTrue(f func() bool, format string, args ...interface{}) {
	// Note, the logger will only be notified here if assertions are enabled
	if l.config.Assertions && !f() {
		err := fmt.Sprintf(format, args...)
		l.log(Fatal, err)
		l.Stop()
		panic(err)
	}
}

// AssertDeferredNoError implements the Logger interface
func (l *Log) AssertDeferredNoError(f func() error) {
	if l.config.Assertions {
		err := f()
		if err != nil {
			l.log(Fatal, "%s", err)
		}
		if l.config.Assertions && err != nil {
			l.Stop()
			panic(err)
		}
	}
}

// StopOnPanic implements the Logger interface
func (l *Log) StopOnPanic() {
	if r := recover(); r != nil {
		l.Fatal("Panicking due to:\n%s\nFrom:\n%s", r, Stacktrace{})
		l.Stop()
		panic(r)
	}
}

// RecoverAndPanic implements the Logger interface
func (l *Log) RecoverAndPanic(f func()) { defer l.StopOnPanic(); f() }

func (l *Log) stopAndExit(exit func()) {
	if r := recover(); r != nil {
		l.Fatal("Panicking due to:\n%s\nFrom:\n%s", r, Stacktrace{})
		l.Stop()
		exit()
	}
}

// RecoverAndExit implements the Logger interface
func (l *Log) RecoverAndExit(f, exit func()) { defer l.stopAndExit(exit); f() }

func (l *Log) SetLogLevel(lvl Level) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.LogLevel = lvl
}

// GetLogLevel ...
func (l *Log) GetLogLevel() Level {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	return l.config.LogLevel
}

// GetDisplayLevel implements the Logger interface
func (l *Log) GetDisplayLevel() Level {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	return l.config.DisplayLevel
}

// SetDisplayLevel implements the Logger interface
func (l *Log) SetDisplayLevel(lvl Level) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisplayLevel = lvl
}

// SetPrefix implements the Logger interface
func (l *Log) SetPrefix(prefix string) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.MsgPrefix = prefix
}

// SetLoggingEnabled implements the Logger interface
func (l *Log) SetLoggingEnabled(enabled bool) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisableLogging = !enabled
}

// SetDisplayingEnabled implements the Logger interface
func (l *Log) SetDisplayingEnabled(enabled bool) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisableDisplaying = !enabled
}

// SetContextualDisplayingEnabled implements the Logger interface
func (l *Log) SetContextualDisplayingEnabled(enabled bool) {
	l.configLock.Lock()
	defer l.configLock.Unlock()

	l.config.DisableContextualDisplaying = !enabled
}

// fileWriter implements the RotatingWriter interface
type fileWriter struct {
	writer *bufio.Writer
	file   *os.File

	config Config
}

// Flush implements the RotatingWriter interface
func (fw *fileWriter) Flush() error {
	return fw.writer.Flush()
}

// Write implements the RotatingWriter interface
func (fw *fileWriter) Write(b []byte) (int, error) {
	return fw.writer.Write(b)
}

// WriteString implements the RotatingWriter interface
func (fw *fileWriter) WriteString(s string) (int, error) {
	return fw.writer.WriteString(s)
}

// Close implements the RotatingWriter interface
func (fw *fileWriter) Close() error {
	return fw.file.Close()
}

// Rotate implements the RotatingWriter interface
func (fw *fileWriter) Rotate() error {
	for i := fw.config.RotationSize - 1; i > 0; i-- {
		sourceFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log.%d", fw.config.LoggerName, i))
		destFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log.%d", fw.config.LoggerName, i+1))
		if _, err := os.Stat(sourceFilename); !errors.Is(err, os.ErrNotExist) {
			if err := os.Rename(sourceFilename, destFilename); err != nil {
				return err
			}
		}
	}
	sourceFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log", fw.config.LoggerName))
	destFilename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log.1", fw.config.LoggerName))
	if err := os.Rename(sourceFilename, destFilename); err != nil {
		return err
	}
	writer, file, err := fw.create()
	if err != nil {
		return err
	}
	fw.file = file
	fw.writer = writer
	return nil
}

// Creates a file if it does not exist or opens it in append mode if it does
func (fw *fileWriter) create() (*bufio.Writer, *os.File, error) {
	filename := filepath.Join(fw.config.Directory, fmt.Sprintf("%s.log", fw.config.LoggerName))
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, perms.ReadWrite)
	if err != nil {
		return nil, nil, err
	}
	writer := bufio.NewWriter(file)
	return writer, file, nil
}

// Initialize implements the RotatingWriter interface
func (fw *fileWriter) Initialize(config Config) (int, error) {
	fw.config = config
	writer, file, err := fw.create()
	if err != nil {
		return 0, err
	}
	fw.writer = writer
	fw.file = file
	fileinfo, err := file.Stat()
	if err != nil {
		return 0, err
	}
	fileSize := fileinfo.Size()
	return int(fileSize), nil
}
<|endoftext|>