<commit_before>/*

aufs driver directory structure

.
├── layers // Metadata of layers
│   ├── 1
│   ├── 2
│   └── 3
├── diffs  // Content of the layer
│   ├── 1  // Contains layers that need to be mounted for the id
│   ├── 2
│   └── 3
└── mnt    // Mount points for the rw layers to be mounted
    ├── 1
    ├── 2
    └── 3

*/

package aufslimit

import (
	"bufio"
	"fmt"
	"github.com/dotcloud/docker/archive"
	"github.com/dotcloud/docker/graphdriver"
	"github.com/dotcloud/docker/utils"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"
)

func init() {
	graphdriver.Register("aufslimit", Init)
}

type Driver struct {
	root string
}

// New returns a new AUFS driver.
// An error is returned if AUFS is not supported.
func Init(root string) (graphdriver.Driver, error) {
	// Try to load the aufs kernel module
	if err := supportsAufs(); err != nil {
		return nil, err
	}
	paths := []string{
		"mnt",
		"diff",
		"layers",
	}

	// Create the root aufs driver dir and return
	// if it already exists
	// If not populate the dir structure
	if err := os.MkdirAll(root, 0755); err != nil {
		if os.IsExist(err) {
			return &Driver{root}, nil
		}
		return nil, err
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
			return nil, err
		}
	}
	return &Driver{root}, nil
}

// Return a nil error if the kernel supports aufs
// We cannot modprobe because inside dind modprobe fails
// to run
func supportsAufs() error {
	// We can try to modprobe aufs first before looking at
	// proc/filesystems for when aufs is supported
	exec.Command("modprobe", "aufs").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		return err
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if strings.Contains(s.Text(), "aufs") {
			return nil
		}
	}
	return fmt.Errorf("AUFS was not found in /proc/filesystems")
}

func (a Driver) rootPath() string {
	return a.root
}

func (Driver) String() string {
	return "aufslimit"
}

func (a Driver) Status() [][2]string {
	ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
	return [][2]string{
		{"Root Dir", a.rootPath()},
		{"Dirs", fmt.Sprintf("%d", len(ids))},
	}
}

// Exists returns true if the given id is registered with
// this driver
func (a Driver) Exists(id string) bool {
	if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
		return false
	}
	return true
}

// Three folders are created for each id
// mnt, layers, and diff
func (a *Driver) Create(id, parent string, config *Config) error {
	log.Printf("We should limit this container to DiskQuota: %d", config.DiskQuota)

	if err := a.createDirsFor(id); err != nil {
		return err
	}
	// Write the layers metadata
	f, err := os.Create(path.Join(a.rootPath(), "layers", id))
	if err != nil {
		return err
	}
	defer f.Close()

	if parent != "" {
		ids, err := getParentIds(a.rootPath(), parent)
		if err != nil {
			return err
		}

		if _, err := fmt.Fprintln(f, parent); err != nil {
			return err
		}
		for _, i := range ids {
			if _, err := fmt.Fprintln(f, i); err != nil {
				return err
			}
		}
	}
	return nil
}

func (a *Driver) createDirsFor(id string) error {
	paths := []string{
		"mnt",
		"diff",
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
			return err
		}
	}
	return nil
}

// Unmount and remove the dir information
func (a *Driver) Remove(id string) error {
	// Make sure the dir is umounted first
	if err := a.unmount(id); err != nil {
		return err
	}
	tmpDirs := []string{
		"mnt",
		"diff",
	}

	// Remove the dirs atomically
	for _, p := range tmpDirs {
		// We need to use a temp dir in the same dir as the driver so Rename
		// does not fall back to the slow copy if /tmp and the driver dir
		// are on different devices
		tmp := path.Join(a.rootPath(), "tmp", p, id)
		if err := os.MkdirAll(tmp, 0755); err != nil {
			return err
		}
		realPath := path.Join(a.rootPath(), p, id)
		if err := os.Rename(realPath, tmp); err != nil && !os.IsNotExist(err) {
			return err
		}
		defer os.RemoveAll(tmp)
	}

	// Remove the layers file for the id
	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Return the rootfs path for the id
// This will mount the dir at it's given path
func (a *Driver) Get(id string) (string, error) {
	ids, err := getParentIds(a.rootPath(), id)
	if err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
		ids = []string{}
	}

	// If a dir does not have a parent ( no layers )do not try to mount
	// just return the diff path to the data
	out := path.Join(a.rootPath(), "diff", id)
	if len(ids) > 0 {
		out = path.Join(a.rootPath(), "mnt", id)
		if err := a.mount(id); err != nil {
			return "", err
		}
	}
	return out, nil
}

// Returns an archive of the contents for the id
func (a *Driver) Diff(id string) (archive.Archive, error) {
	return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
		Recursive:   true,
		Compression: archive.Uncompressed,
	})
}

func (a *Driver) ApplyDiff(id string, diff archive.Archive) error {
	return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
}

// Returns the size of the contents for the id
func (a *Driver) DiffSize(id string) (int64, error) {
	return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) Changes(id string) ([]archive.Change, error) {
	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return nil, err
	}
	return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
	parentIds, err := getParentIds(a.rootPath(), id)
	if err != nil {
		return nil, err
	}
	if len(parentIds) == 0 {
		return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
	}
	layers := make([]string, len(parentIds))

	// Get the diff paths for all the parent ids
	for i, p := range parentIds {
		layers[i] = path.Join(a.rootPath(), "diff", p)
	}
	return layers, nil
}

func (a *Driver) mount(id string) error {
	// If the id is mounted or we get an error return
	if mounted, err := a.mounted(id); err != nil || mounted {
		return err
	}

	var (
		target = path.Join(a.rootPath(), "mnt", id)
		rw     = path.Join(a.rootPath(), "diff", id)
	)

	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return err
	}

	if err := a.aufsMount(layers, rw, target); err != nil {
		return err
	}
	return nil
}

func (a *Driver) unmount(id string) error {
	if mounted, err := a.mounted(id); err != nil || !mounted {
		return err
	}
	target := path.Join(a.rootPath(), "mnt", id)
	return Unmount(target)
}

func (a *Driver) mounted(id string) (bool, error) {
	target := path.Join(a.rootPath(), "mnt", id)
	return Mounted(target)
}

// During cleanup aufs needs to unmount all mountpoints
func (a *Driver) Cleanup() error {
	ids, err := loadIds(path.Join(a.rootPath(), "layers"))
	if err != nil {
		return err
	}
	for _, id := range ids {
		if err := a.unmount(id); err != nil {
			utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
		}
	}
	return nil
}

func (a *Driver) aufsMount(ro []string, rw, target string) error {
	rwBranch := fmt.Sprintf("%v=rw", rw)
	roBranches := ""
	for _, layer := range ro {
		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
	}
	branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)

	//if error, try to load aufs kernel module
	if err := mount("none", target, "aufs", 0, branches); err != nil {
		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
			return fmt.Errorf("Unable to load the AUFS module")
		}
		log.Printf("...module loaded.")
		if err := mount("none", target, "aufs", 0, branches); err != nil {
			return fmt.Errorf("Unable to mount using aufs %s", err)
		}
	}
	return nil
}
<commit_msg>fix dependency issues<commit_after>/*

aufs driver directory structure

.
├── layers // Metadata of layers
│   ├── 1
│   ├── 2
│   └── 3
├── diffs  // Content of the layer
│   ├── 1  // Contains layers that need to be mounted for the id
│   ├── 2
│   └── 3
└── mnt    // Mount points for the rw layers to be mounted
    ├── 1
    ├── 2
    └── 3

*/

package aufslimit

import (
	"bufio"
	"fmt"
	"github.com/dotcloud/docker"
	"github.com/dotcloud/docker/archive"
	"github.com/dotcloud/docker/graphdriver"
	"github.com/dotcloud/docker/utils"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

func init() {
	graphdriver.Register("aufslimit", Init)
}

type Driver struct {
	root string
}

// New returns a new AUFS driver.
// An error is returned if AUFS is not supported.
func Init(root string) (graphdriver.Driver, error) {
	// Try to load the aufs kernel module
	if err := supportsAufs(); err != nil {
		return nil, err
	}
	paths := []string{
		"mnt",
		"diff",
		"layers",
	}

	// Create the root aufs driver dir and return
	// if it already exists
	// If not populate the dir structure
	if err := os.MkdirAll(root, 0755); err != nil {
		if os.IsExist(err) {
			return &Driver{root}, nil
		}
		return nil, err
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
			return nil, err
		}
	}
	return &Driver{root}, nil
}

// Return a nil error if the kernel supports aufs
// We cannot modprobe because inside dind modprobe fails
// to run
func supportsAufs() error {
	// We can try to modprobe aufs first before looking at
	// proc/filesystems for when aufs is supported
	exec.Command("modprobe", "aufs").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		return err
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if strings.Contains(s.Text(), "aufs") {
			return nil
		}
	}
	return fmt.Errorf("AUFS was not found in /proc/filesystems")
}

func (a Driver) rootPath() string {
	return a.root
}

func (Driver) String() string {
	return "aufslimit"
}

func (a Driver) Status() [][2]string {
	ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
	return [][2]string{
		{"Root Dir", a.rootPath()},
		{"Dirs", fmt.Sprintf("%d", len(ids))},
	}
}

// Exists returns true if the given id is registered with
// this driver
func (a Driver) Exists(id string) bool {
	if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
		return false
	}
	return true
}

// Three folders are created for each id
// mnt, layers, and diff
func (a *Driver) Create(id, parent string, config *Config) error {
	log.Printf("We should limit this container to DiskQuota: %d", config.DiskQuota)

	if err := a.createDirsFor(id); err != nil {
		return err
	}
	// Write the layers metadata
	f, err := os.Create(path.Join(a.rootPath(), "layers", id))
	if err != nil {
		return err
	}
	defer f.Close()

	if parent != "" {
		ids, err := getParentIds(a.rootPath(), parent)
		if err != nil {
			return err
		}

		if _, err := fmt.Fprintln(f, parent); err != nil {
			return err
		}
		for _, i := range ids {
			if _, err := fmt.Fprintln(f, i); err != nil {
				return err
			}
		}
	}
	return nil
}

func (a *Driver) createDirsFor(id string) error {
	paths := []string{
		"mnt",
		"diff",
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
			return err
		}
	}
	return nil
}

// Unmount and remove the dir information
func (a *Driver) Remove(id string) error {
	// Make sure the dir is umounted first
	if err := a.unmount(id); err != nil {
		return err
	}
	tmpDirs := []string{
		"mnt",
		"diff",
	}

	// Remove the dirs atomically
	for _, p := range tmpDirs {
		// We need to use a temp dir in the same dir as the driver so Rename
		// does not fall back to the slow copy if /tmp and the driver dir
		// are on different devices
		tmp := path.Join(a.rootPath(), "tmp", p, id)
		if err := os.MkdirAll(tmp, 0755); err != nil {
			return err
		}
		realPath := path.Join(a.rootPath(), p, id)
		if err := os.Rename(realPath, tmp); err != nil && !os.IsNotExist(err) {
			return err
		}
		defer os.RemoveAll(tmp)
	}

	// Remove the layers file for the id
	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Return the rootfs path for the id
// This will mount the dir at it's given path
func (a *Driver) Get(id string) (string, error) {
	ids, err := getParentIds(a.rootPath(), id)
	if err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
		ids = []string{}
	}

	// If a dir does not have a parent ( no layers )do not try to mount
	// just return the diff path to the data
	out := path.Join(a.rootPath(), "diff", id)
	if len(ids) > 0 {
		out = path.Join(a.rootPath(), "mnt", id)
		if err := a.mount(id); err != nil {
			return "", err
		}
	}
	return out, nil
}

// Returns an archive of the contents for the id
func (a *Driver) Diff(id string) (archive.Archive, error) {
	return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
		Recursive:   true,
		Compression: archive.Uncompressed,
	})
}

func (a *Driver) ApplyDiff(id string, diff archive.Archive) error {
	return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
}

// Returns the size of the contents for the id
func (a *Driver) DiffSize(id string) (int64, error) {
	return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) Changes(id string) ([]archive.Change, error) {
	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return nil, err
	}
	return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
	parentIds, err := getParentIds(a.rootPath(), id)
	if err != nil {
		return nil, err
	}
	if len(parentIds) == 0 {
		return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
	}
	layers := make([]string, len(parentIds))

	// Get the diff paths for all the parent ids
	for i, p := range parentIds {
		layers[i] = path.Join(a.rootPath(), "diff", p)
	}
	return layers, nil
}

func (a *Driver) mount(id string) error {
	// If the id is mounted or we get an error return
	if mounted, err := a.mounted(id); err != nil || mounted {
		return err
	}

	var (
		target = path.Join(a.rootPath(), "mnt", id)
		rw     = path.Join(a.rootPath(), "diff", id)
	)

	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return err
	}

	if err := a.aufsMount(layers, rw, target); err != nil {
		return err
	}
	return nil
}

func (a *Driver) unmount(id string) error {
	if mounted, err := a.mounted(id); err != nil || !mounted {
		return err
	}
	target := path.Join(a.rootPath(), "mnt", id)
	return Unmount(target)
}

func (a *Driver) mounted(id string) (bool, error) {
	target := path.Join(a.rootPath(), "mnt", id)
	return Mounted(target)
}

// During cleanup aufs needs to unmount all mountpoints
func (a *Driver) Cleanup() error {
	ids, err := loadIds(path.Join(a.rootPath(), "layers"))
	if err != nil {
		return err
	}
	for _, id := range ids {
		if err := a.unmount(id); err != nil {
			utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
		}
	}
	return nil
}

func (a *Driver) aufsMount(ro []string, rw, target string) error {
	rwBranch := fmt.Sprintf("%v=rw", rw)
	roBranches := ""
	for _, layer := range ro {
		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
	}
	branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)

	//if error, try to load aufs kernel module
	if err := mount("none", target, "aufs", 0, branches); err != nil {
		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
			return fmt.Errorf("Unable to load the AUFS module")
		}
		log.Printf("...module loaded.")
		if err := mount("none", target, "aufs", 0, branches); err != nil {
			return fmt.Errorf("Unable to mount using aufs %s", err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package acceptance

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf/pivnet-resource/concourse"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func run(command *exec.Cmd, stdinContents []byte) *gexec.Session {
	fmt.Fprintf(os.Stdout, "input: %s\n", stdinContents)

	stdin, err := command.StdinPipe()
	Expect(err).ShouldNot(HaveOccurred())

	session, err := gexec.Start(command, os.Stdout, os.Stdout)
	Expect(err).NotTo(HaveOccurred())

	_, err = io.WriteString(stdin, string(stdinContents))
	Expect(err).ShouldNot(HaveOccurred())

	err = stdin.Close()
	Expect(err).ShouldNot(HaveOccurred())

	return session
}

func metadataValueForKey(metadata []concourse.Metadata, name string) (string, error) {
	for _, i := range metadata {
		if i.Name == name {
			return i.Value, nil
		}
	}
	return "", fmt.Errorf("name not found: %s", name)
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func versionsWithoutFingerprints(versionsWithFingerprints []string) []string {
	versionsWithoutFingerprints := make([]string, len(versionsWithFingerprints))
	for i, v := range versionsWithFingerprints {
		split := strings.Split(v, "#")
		versionsWithoutFingerprints[i] = split[0]
	}
	return versionsWithoutFingerprints
}
<commit_msg>WIP = debug<commit_after>package acceptance

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf/pivnet-resource/concourse"

	. "github.com/onsi/gomega"
)

func run(command *exec.Cmd, stdinContents []byte) *gexec.Session {
	fmt.Fprintf(os.Stdout, "input: %s\n", stdinContents)

	stdin, err := command.StdinPipe()
	Expect(err).ShouldNot(HaveOccurred())

	session, err := gexec.Start(command, os.Stdout, os.Stdout)
	Expect(err).NotTo(HaveOccurred())

	_, err = io.WriteString(stdin, string(stdinContents))
	Expect(err).ShouldNot(HaveOccurred())

	err = stdin.Close()
	Expect(err).ShouldNot(HaveOccurred())

	return session
}

func metadataValueForKey(metadata []concourse.Metadata, name string) (string, error) {
	for _, i := range metadata {
		if i.Name == name {
			return i.Value, nil
		}
	}
	return "", fmt.Errorf("name not found: %s", name)
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func versionsWithoutFingerprints(versionsWithFingerprints []string) []string {
	versionsWithoutFingerprints := make([]string, len(versionsWithFingerprints))
	for i, v := range versionsWithFingerprints {
		split := strings.Split(v, "#")
		versionsWithoutFingerprints[i] = split[0]
	}
	return versionsWithoutFingerprints
}
<|endoftext|>


<commit_before>package workflow

import (
	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/sdk"
)

// LoadWorkfowArtifactByHash retrieves an artiface using its download hash
func LoadWorkfowArtifactByHash(db gorp.SqlExecutor, hash string) (*sdk.WorkflowNodeRunArtifact, error) {
	var artGorp NodeRunArtifact
	query := `SELECT
				id,
				name,
				tag,
				workflow_node_run_id,
				download_hash,
				size,
				perm,
				md5sum,
				object_path,
				created,
				workflow_run_id,
				coalesce(sha512sum, '')
		  FROM workflow_node_run_artifacts
		  WHERE workflow_node_run_artifacts.download_hash = $1`
	if err := db.SelectOne(&artGorp, query, hash); err != nil {
		return nil, err
	}
	art := sdk.WorkflowNodeRunArtifact(artGorp)
	return &art, nil

}

// LoadArtifactByIDs Load artifact by workflow ID and artifact ID
func LoadArtifactByIDs(db gorp.SqlExecutor, workflowID, artifactID int64) (*sdk.WorkflowNodeRunArtifact, error) {
	var artGorp NodeRunArtifact
	query := `
		SELECT
			workflow_node_run_artifacts.id,
			workflow_node_run_artifacts.name,
			workflow_node_run_artifacts.tag,
			workflow_node_run_artifacts.workflow_node_run_id,
			workflow_node_run_artifacts.download_hash,
			workflow_node_run_artifacts.size,
			workflow_node_run_artifacts.perm,
			workflow_node_run_artifacts.md5sum,
			workflow_node_run_artifacts.object_path,
			workflow_node_run_artifacts.created,
			workflow_node_run_artifacts.workflow_run_id,
			coalesce(workflow_node_run_artifacts.sha512sum, '')
		FROM workflow_node_run_artifacts
		JOIN workflow_run ON workflow_run.id = workflow_node_run_artifacts.workflow_run_id
		WHERE workflow_run.workflow_id = $1 AND workflow_node_run_artifacts.id = $2

	`
	if err := db.SelectOne(&artGorp, query, workflowID, artifactID); err != nil {
		return nil, err
	}
	art := sdk.WorkflowNodeRunArtifact(artGorp)
	return &art, nil
}

func loadArtifactByNodeRunID(db gorp.SqlExecutor, nodeRunID int64) ([]sdk.WorkflowNodeRunArtifact, error) {
	var artifactsGorp []NodeRunArtifact
	if _, err := db.Select(&artifactsGorp, `SELECT
			id,
			name,
			tag,
			workflow_node_run_id,
			download_hash,
			size,
			perm,
			md5sum,
			object_path,
			created,
			workflow_run_id,
			coalesce(sha512sum, '')
		FROM workflow_node_run_artifacts WHERE workflow_node_run_id = $1`, nodeRunID); err != nil {
		return nil, err
	}

	artifacts := make([]sdk.WorkflowNodeRunArtifact, len(artifactsGorp))
	for i := range artifactsGorp {
		artifacts[i] = sdk.WorkflowNodeRunArtifact(artifactsGorp[i])
	}
	return artifacts, nil
}

// InsertArtifact insert in table workflow_artifacts
func InsertArtifact(db gorp.SqlExecutor, a *sdk.WorkflowNodeRunArtifact) error {
	wArtifactDB := NodeRunArtifact(*a)
	if err := db.Insert(&wArtifactDB); err != nil {
		return err
	}
	a.ID = wArtifactDB.ID
	return nil
}
<commit_msg>fix(api): fix query on artifacts (#2758)<commit_after>package workflow

import (
	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/sdk"
)

// LoadWorkfowArtifactByHash retrieves an artiface using its download hash
func LoadWorkfowArtifactByHash(db gorp.SqlExecutor, hash string) (*sdk.WorkflowNodeRunArtifact, error) {
	var artGorp NodeRunArtifact
	query := `SELECT
				id,
				name,
				tag,
				workflow_node_run_id,
				download_hash,
				size,
				perm,
				md5sum,
				object_path,
				created,
				workflow_run_id,
				coalesce(sha512sum, '') AS sha512sum
		  FROM workflow_node_run_artifacts
		  WHERE workflow_node_run_artifacts.download_hash = $1`
	if err := db.SelectOne(&artGorp, query, hash); err != nil {
		return nil, err
	}
	art := sdk.WorkflowNodeRunArtifact(artGorp)
	return &art, nil

}

// LoadArtifactByIDs Load artifact by workflow ID and artifact ID
func LoadArtifactByIDs(db gorp.SqlExecutor, workflowID, artifactID int64) (*sdk.WorkflowNodeRunArtifact, error) {
	var artGorp NodeRunArtifact
	query := `
		SELECT
			workflow_node_run_artifacts.id,
			workflow_node_run_artifacts.name,
			workflow_node_run_artifacts.tag,
			workflow_node_run_artifacts.workflow_node_run_id,
			workflow_node_run_artifacts.download_hash,
			workflow_node_run_artifacts.size,
			workflow_node_run_artifacts.perm,
			workflow_node_run_artifacts.md5sum,
			workflow_node_run_artifacts.object_path,
			workflow_node_run_artifacts.created,
			workflow_node_run_artifacts.workflow_run_id,
			coalesce(workflow_node_run_artifacts.sha512sum, '') AS sha512sum
		FROM workflow_node_run_artifacts
		JOIN workflow_run ON workflow_run.id = workflow_node_run_artifacts.workflow_run_id
		WHERE workflow_run.workflow_id = $1 AND workflow_node_run_artifacts.id = $2

	`
	if err := db.SelectOne(&artGorp, query, workflowID, artifactID); err != nil {
		return nil, err
	}
	art := sdk.WorkflowNodeRunArtifact(artGorp)
	return &art, nil
}

func loadArtifactByNodeRunID(db gorp.SqlExecutor, nodeRunID int64) ([]sdk.WorkflowNodeRunArtifact, error) {
	var artifactsGorp []NodeRunArtifact
	if _, err := db.Select(&artifactsGorp, `SELECT
			id,
			name,
			tag,
			workflow_node_run_id,
			download_hash,
			size,
			perm,
			md5sum,
			object_path,
			created,
			workflow_run_id,
			coalesce(sha512sum, '') AS sha512sum
		FROM workflow_node_run_artifacts WHERE workflow_node_run_id = $1`, nodeRunID); err != nil {
		return nil, err
	}

	artifacts := make([]sdk.WorkflowNodeRunArtifact, len(artifactsGorp))
	for i := range artifactsGorp {
		artifacts[i] = sdk.WorkflowNodeRunArtifact(artifactsGorp[i])
	}
	return artifacts, nil
}

// InsertArtifact insert in table workflow_artifacts
func InsertArtifact(db gorp.SqlExecutor, a *sdk.WorkflowNodeRunArtifact) error {
	wArtifactDB := NodeRunArtifact(*a)
	if err := db.Insert(&wArtifactDB); err != nil {
		return err
	}
	a.ID = wArtifactDB.ID
	return nil
}
<|endoftext|>


<commit_before>package fronted

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"sync"
	"time"
)

const (
	NumWorkers = 10 // number of worker goroutines for verifying
)

// Masquerade contains the data for a single masquerade host, including
// the domain and the root CA.
type Masquerade struct {
	// Domain: the domain to use for domain fronting
	Domain string

	// IpAddress: pre-resolved ip address to use instead of Domain (if
	// available)
	IpAddress string
}

type MasqueradeSet []*Masquerade

// verifiedMasqueradeSet represents a set of Masquerade configurations.
// verifiedMasqueradeSet verifies each configured Masquerade by attempting to
// proxy using it.
type verifiedMasqueradeSet struct {
	dialer             *dialer
	candidatesCh       chan *Masquerade
	stopCh             chan interface{}
	verifiedChSize     int
	verifiedCh         chan *Masquerade
	verifiedCount      int
	verifiedCountMutex sync.Mutex
	wg                 sync.WaitGroup
}

// nextVerified returns the next available verified *Masquerade, blocking until
// such is available.  The masquerade is immediately written back onto
// verifiedCh, turning verifiedCh into a sort of cyclic queue.
func (vms *verifiedMasqueradeSet) nextVerified() *Masquerade {
	masquerade := <-vms.verifiedCh
	vms.verifiedCh <- masquerade
	return masquerade
}

// verified sets up a new verifiedMasqueradeSet that verifies each of the
// Masquerades in this MasqueradeSet for the given Dialer.
func (d *dialer) verifiedMasquerades() *verifiedMasqueradeSet {
	// Size verifiedChSize to be able to hold the smaller of MaxMasquerades or
	// the number of configured masquerades.
	verifiedChSize := len(d.Masquerades)
	if d.MaxMasquerades < verifiedChSize {
		verifiedChSize = d.MaxMasquerades
	}
	log.Debugf("Verifiying up to %d masquerades", verifiedChSize)

	vms := &verifiedMasqueradeSet{
		dialer:         d,
		candidatesCh:   make(chan *Masquerade),
		stopCh:         make(chan interface{}, 1),
		verifiedChSize: verifiedChSize,
		verifiedCh:     make(chan *Masquerade, verifiedChSize),
	}
	vms.wg.Add(NumWorkers)
	// Spawn some worker goroutines to verify masquerades
	for i := 0; i < NumWorkers; i++ {
		go vms.verify()
	}

	// Feed candidates for verification
	go vms.feedCandidates()

	return vms
}

// feedCandidates feeds the candidate masquerades to our worker routines in
// random order
func (vms *verifiedMasqueradeSet) feedCandidates() {
	for _, i := range rand.Perm(len(vms.dialer.Masquerades)) {
		if !vms.feedCandidate(vms.dialer.Masquerades[i]) {
			break
		}
	}
	close(vms.candidatesCh)
}

func (vms *verifiedMasqueradeSet) feedCandidate(candidate *Masquerade) bool {
	select {
	case <-vms.stopCh:
		log.Trace("Received stop, not feeding any further")
		return false
	case vms.candidatesCh <- candidate:
		log.Trace("Fed candidate")
		return true
	}
}

// stop stops the verification process
func (vms *verifiedMasqueradeSet) stop() {
	log.Trace("masquerades stop called")
	vms.stopCh <- nil
	log.Trace("masquerades waiting for workers to finish")
	vms.wg.Wait()
	log.Trace("masquerades stopped")
}

// verify checks masquerades obtained from candidatesCh to see if they work on
// the test server.
func (vms *verifiedMasqueradeSet) verify() {
	for {
		candidate, ok := <-vms.candidatesCh
		if !ok {
			vms.wg.Done()
			return
		}
		if !vms.doVerify(candidate) {
			return
		}
	}
}

// doVerify does the verification and returns a boolean indicating whether or
// not to continue processing more verifications.
func (vms *verifiedMasqueradeSet) doVerify(masquerade *Masquerade) bool {
	errCh := make(chan error, 2)
	go func() {
		// Limit amount of time we'll wait for a response
		time.Sleep(30 * time.Second)
		errCh <- fmt.Errorf("Timed out verifying %s", masquerade.Domain)
	}()
	go func() {
		start := time.Now()
		httpClient := vms.dialer.HttpClientUsing(masquerade)
		req, _ := http.NewRequest("HEAD", "http://www.google.com/humans.txt", nil)
		resp, err := httpClient.Do(req)
		if err != nil {
			errCh <- fmt.Errorf("HTTP ERROR FOR MASQUERADE %v: %v", masquerade.Domain, err)
			return
		} else {
			body, err := ioutil.ReadAll(resp.Body)
			defer resp.Body.Close()
			if err != nil {
				errCh <- fmt.Errorf("HTTP Body Error: %s", body)
			} else {
				delta := time.Now().Sub(start)
				log.Tracef("SUCCESSFUL CHECK FOR: %s IN %s, %s", masquerade.Domain, delta, body)
				errCh <- nil
			}
		}
	}()
	err := <-errCh
	if err != nil {
		log.Error(err)
		return true
	}
	if vms.incrementVerifiedCount() {
		vms.verifiedCh <- masquerade
		return true
	}
	return false
}

// incrementVerifiedCount keeps track of the number of verified masquerades and
// caps it at MaxMasquerades.
func (vms *verifiedMasqueradeSet) incrementVerifiedCount() bool {
	vms.verifiedCountMutex.Lock()
	defer vms.verifiedCountMutex.Unlock()
	if vms.verifiedCount == vms.verifiedChSize {
		return false
	}
	vms.verifiedCount += 1
	return true
}
<commit_msg>Fixed spelling mistake in fronted log<commit_after>package fronted

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"sync"
	"time"
)

const (
	NumWorkers = 10 // number of worker goroutines for verifying
)

// Masquerade contains the data for a single masquerade host, including
// the domain and the root CA.
type Masquerade struct {
	// Domain: the domain to use for domain fronting
	Domain string

	// IpAddress: pre-resolved ip address to use instead of Domain (if
	// available)
	IpAddress string
}

type MasqueradeSet []*Masquerade

// verifiedMasqueradeSet represents a set of Masquerade configurations.
// verifiedMasqueradeSet verifies each configured Masquerade by attempting to
// proxy using it.
type verifiedMasqueradeSet struct {
	dialer             *dialer
	candidatesCh       chan *Masquerade
	stopCh             chan interface{}
	verifiedChSize     int
	verifiedCh         chan *Masquerade
	verifiedCount      int
	verifiedCountMutex sync.Mutex
	wg                 sync.WaitGroup
}

// nextVerified returns the next available verified *Masquerade, blocking until
// such is available.  The masquerade is immediately written back onto
// verifiedCh, turning verifiedCh into a sort of cyclic queue.
func (vms *verifiedMasqueradeSet) nextVerified() *Masquerade {
	masquerade := <-vms.verifiedCh
	vms.verifiedCh <- masquerade
	return masquerade
}

// verified sets up a new verifiedMasqueradeSet that verifies each of the
// Masquerades in this MasqueradeSet for the given Dialer.
func (d *dialer) verifiedMasquerades() *verifiedMasqueradeSet {
	// Size verifiedChSize to be able to hold the smaller of MaxMasquerades or
	// the number of configured masquerades.
	verifiedChSize := len(d.Masquerades)
	if d.MaxMasquerades < verifiedChSize {
		verifiedChSize = d.MaxMasquerades
	}
	log.Debugf("Verifying up to %d masquerades", verifiedChSize)

	vms := &verifiedMasqueradeSet{
		dialer:         d,
		candidatesCh:   make(chan *Masquerade),
		stopCh:         make(chan interface{}, 1),
		verifiedChSize: verifiedChSize,
		verifiedCh:     make(chan *Masquerade, verifiedChSize),
	}
	vms.wg.Add(NumWorkers)
	// Spawn some worker goroutines to verify masquerades
	for i := 0; i < NumWorkers; i++ {
		go vms.verify()
	}

	// Feed candidates for verification
	go vms.feedCandidates()

	return vms
}

// feedCandidates feeds the candidate masquerades to our worker routines in
// random order
func (vms *verifiedMasqueradeSet) feedCandidates() {
	for _, i := range rand.Perm(len(vms.dialer.Masquerades)) {
		if !vms.feedCandidate(vms.dialer.Masquerades[i]) {
			break
		}
	}
	close(vms.candidatesCh)
}

func (vms *verifiedMasqueradeSet) feedCandidate(candidate *Masquerade) bool {
	select {
	case <-vms.stopCh:
		log.Trace("Received stop, not feeding any further")
		return false
	case vms.candidatesCh <- candidate:
		log.Trace("Fed candidate")
		return true
	}
}

// stop stops the verification process
func (vms *verifiedMasqueradeSet) stop() {
	log.Trace("masquerades stop called")
	vms.stopCh <- nil
	log.Trace("masquerades waiting for workers to finish")
	vms.wg.Wait()
	log.Trace("masquerades stopped")
}

// verify checks masquerades obtained from candidatesCh to see if they work on
// the test server.
func (vms *verifiedMasqueradeSet) verify() {
	for {
		candidate, ok := <-vms.candidatesCh
		if !ok {
			vms.wg.Done()
			return
		}
		if !vms.doVerify(candidate) {
			return
		}
	}
}

// doVerify does the verification and returns a boolean indicating whether or
// not to continue processing more verifications.
func (vms *verifiedMasqueradeSet) doVerify(masquerade *Masquerade) bool {
	errCh := make(chan error, 2)
	go func() {
		// Limit amount of time we'll wait for a response
		time.Sleep(30 * time.Second)
		errCh <- fmt.Errorf("Timed out verifying %s", masquerade.Domain)
	}()
	go func() {
		start := time.Now()
		httpClient := vms.dialer.HttpClientUsing(masquerade)
		req, _ := http.NewRequest("HEAD", "http://www.google.com/humans.txt", nil)
		resp, err := httpClient.Do(req)
		if err != nil {
			errCh <- fmt.Errorf("HTTP ERROR FOR MASQUERADE %v: %v", masquerade.Domain, err)
			return
		} else {
			body, err := ioutil.ReadAll(resp.Body)
			defer resp.Body.Close()
			if err != nil {
				errCh <- fmt.Errorf("HTTP Body Error: %s", body)
			} else {
				delta := time.Now().Sub(start)
				log.Tracef("SUCCESSFUL CHECK FOR: %s IN %s, %s", masquerade.Domain, delta, body)
				errCh <- nil
			}
		}
	}()
	err := <-errCh
	if err != nil {
		log.Error(err)
		return true
	}
	if vms.incrementVerifiedCount() {
		vms.verifiedCh <- masquerade
		return true
	}
	return false
}

// incrementVerifiedCount keeps track of the number of verified masquerades and
// caps it at MaxMasquerades.
func (vms *verifiedMasqueradeSet) incrementVerifiedCount() bool {
	vms.verifiedCountMutex.Lock()
	defer vms.verifiedCountMutex.Unlock()
	if vms.verifiedCount == vms.verifiedChSize {
		return false
	}
	vms.verifiedCount += 1
	return true
}
<|endoftext|>


<commit_before>package memtable

import (
	"math/rand"

	"github.com/kezhuw/leveldb/internal/batch"
	"github.com/kezhuw/leveldb/internal/errors"
	"github.com/kezhuw/leveldb/internal/iterator"
	"github.com/kezhuw/leveldb/internal/keys"
)

const (
	maxHeight = 12
)

const (
	bytesBlockSize = 4096
	nextsBlockSize = 1024
	nodesBlockSize = 512

	largeBytesSize = 1024
)

type node struct {
	ikey  []byte
	value []byte // Nil if deleted.
	nexts []*node
}

type MemTable struct {
	rnd *rand.Rand

	height int
	head   *node
	prevs  [maxHeight]*node
	icmp   *keys.InternalComparator

	usage int
	bytes []byte
	nexts []*node
	nodes []node

	mutex rwmutex
}

func (m *MemTable) allocBytes(n int) []byte {
	m.usage += n
	len, cap := len(m.bytes), cap(m.bytes)
	size := len + n
	if size <= cap {
		m.bytes = m.bytes[:size]
		return m.bytes[len:size:size]
	}
	if n >= largeBytesSize {
		return make([]byte, n)
	}
	m.bytes = make([]byte, n, n+bytesBlockSize)
	return m.bytes[0:n:n]
}

func (m *MemTable) allocNexts(n int) []*node {
	i, j := len(m.nexts), cap(m.nexts)
	if k := i + n; k <= j {
		m.nexts = m.nexts[:k]
		return m.nexts[i:k]
	}
	m.nexts = make([]*node, n, n+nextsBlockSize)
	return m.nexts
}

func (m *MemTable) allocNode() *node {
	if i := len(m.nodes); i < cap(m.nodes) {
		m.nodes = m.nodes[:i+1]
		return &m.nodes[i]
	}
	m.nodes = make([]node, 1, 1+nodesBlockSize)
	return &m.nodes[0]
}

func (m *MemTable) newNode(h int) *node {
	n := m.allocNode()
	n.nexts = m.allocNexts(h)
	return n
}

func (m *MemTable) randomHeight() int {
	h := 1
	if h < maxHeight && m.rnd.Intn(4) == 0 {
		h++
	}
	return h
}

func (m *MemTable) findLast() *node {
	p := m.head
	for h := m.height - 1; h >= 0; h-- {
		for {
			n := p.Next(h)
			if n == nil {
				break
			}
			p = n
		}
	}
	if p == m.head {
		return nil
	}
	return p
}

func (m *MemTable) findLessThan(ikey []byte) *node {
	p := m.head
	for h := m.height - 1; h >= 0; h-- {
		for {
			n := p.Next(h)
			if n == nil {
				break
			}
			if m.icmp.Compare(n.ikey, ikey) >= 0 {
				break
			}
			p = n
		}
	}
	if p == m.head {
		return nil
	}
	return p
}

func (m *MemTable) findGreaterOrEqual(ikey []byte, prevs []*node) (n *node, hit bool) {
	p := m.head
	for h := m.height - 1; h >= 0; h-- {
		n = p.Next(h)
		for {
			if n == nil {
				break
			}
			if r := m.icmp.Compare(ikey, n.ikey); r <= 0 {
				hit = r == 0
				break
			}
			p, n = n, n.Next(h)
		}
		prevs[h] = p
	}
	return
}

func (m *MemTable) ApproximateMemoryUsage() int {
	return m.usage
}

func (m *MemTable) Empty() bool {
	return m.head.Next(0) == nil
}

func (m *MemTable) Batch(seq keys.Sequence, items []batch.Item) {
	for _, item := range items {
		switch item.Value {
		case nil:
			m.Add(seq, keys.Delete, item.Key, nil)
		default:
			m.Add(seq, keys.Value, item.Key, item.Value)
		}
		seq++
	}
}

func (m *MemTable) Add(seq keys.Sequence, kind keys.Kind, key, value []byte) {
	b := m.allocBytes(len(key) + keys.TagLen + len(value))
	ikey := b[:len(key)+keys.TagLen]
	keys.MakeInternalKey(ikey, key, seq, kind)

	prevs := m.prevs[:]
	_, hit := m.findGreaterOrEqual(ikey, prevs)
	if hit {
		panic("duplicated key in MemTable")
	}

	n := m.allocNode()
	n.ikey = ikey
	n.value = b[len(ikey):]
	switch kind {
	case keys.Delete:
		n.value = nil
	default:
		copy(n.value, value)
	}

	h := m.randomHeight()
	n.nexts = m.allocNexts(h)

	if m.height < h {
		for i := m.height; i < h; i++ {
			prevs[i] = m.head
		}
		m.height = h
	}

	m.mutex.Lock()
	for i := 0; i < h; i++ {
		n.nexts[i] = prevs[i].nexts[i]
		prevs[i].SetNext(i, n)
	}
	m.mutex.Unlock()
}

func (m *MemTable) Get(ikey keys.InternalKey) (value []byte, err error, ok bool) {
	var prevs [maxHeight]*node
	m.mutex.RLock()
	n, hit := m.findGreaterOrEqual(ikey, prevs[:])
	m.mutex.RUnlock()
	if n != nil && (hit || m.icmp.UserKeyComparator.Compare(ikey.UserKey(), keys.InternalKey(n.ikey).UserKey()) == 0) {
		if n.value == nil {
			err = errors.ErrNotFound
		}
		return n.value, err, true
	}
	return nil, nil, false
}

func (m *MemTable) NewIterator() iterator.Iterator {
	return &memtableIterator{m: m}
}

func New(icmp *keys.InternalComparator) *MemTable {
	m := &MemTable{
		rnd:    rand.New(rand.NewSource(rand.Int63())),
		height: 1,
		icmp:   icmp,
	}
	m.head = m.newNode(maxHeight)
	return m
}
<commit_msg>Fix 'if' to 'for' in (*MemTable).randomHeight<commit_after>package memtable

import (
	"math/rand"

	"github.com/kezhuw/leveldb/internal/batch"
	"github.com/kezhuw/leveldb/internal/errors"
	"github.com/kezhuw/leveldb/internal/iterator"
	"github.com/kezhuw/leveldb/internal/keys"
)

const (
	maxHeight = 12
)

const (
	bytesBlockSize = 4096
	nextsBlockSize = 1024
	nodesBlockSize = 512

	largeBytesSize = 1024
)

type node struct {
	ikey  []byte
	value []byte // Nil if deleted.
	nexts []*node
}

type MemTable struct {
	rnd *rand.Rand

	height int
	head   *node
	prevs  [maxHeight]*node
	icmp   *keys.InternalComparator

	usage int
	bytes []byte
	nexts []*node
	nodes []node

	mutex rwmutex
}

func (m *MemTable) allocBytes(n int) []byte {
	m.usage += n
	len, cap := len(m.bytes), cap(m.bytes)
	size := len + n
	if size <= cap {
		m.bytes = m.bytes[:size]
		return m.bytes[len:size:size]
	}
	if n >= largeBytesSize {
		return make([]byte, n)
	}
	m.bytes = make([]byte, n, n+bytesBlockSize)
	return m.bytes[0:n:n]
}

func (m *MemTable) allocNexts(n int) []*node {
	i, j := len(m.nexts), cap(m.nexts)
	if k := i + n; k <= j {
		m.nexts = m.nexts[:k]
		return m.nexts[i:k]
	}
	m.nexts = make([]*node, n, n+nextsBlockSize)
	return m.nexts
}

func (m *MemTable) allocNode() *node {
	if i := len(m.nodes); i < cap(m.nodes) {
		m.nodes = m.nodes[:i+1]
		return &m.nodes[i]
	}
	m.nodes = make([]node, 1, 1+nodesBlockSize)
	return &m.nodes[0]
}

func (m *MemTable) newNode(h int) *node {
	n := m.allocNode()
	n.nexts = m.allocNexts(h)
	return n
}

func (m *MemTable) randomHeight() int {
	h := 1
	for h < maxHeight && m.rnd.Intn(4) == 0 {
		h++
	}
	return h
}

func (m *MemTable) findLast() *node {
	p := m.head
	for h := m.height - 1; h >= 0; h-- {
		for {
			n := p.Next(h)
			if n == nil {
				break
			}
			p = n
		}
	}
	if p == m.head {
		return nil
	}
	return p
}

func (m *MemTable) findLessThan(ikey []byte) *node {
	p := m.head
	for h := m.height - 1; h >= 0; h-- {
		for {
			n := p.Next(h)
			if n == nil {
				break
			}
			if m.icmp.Compare(n.ikey, ikey) >= 0 {
				break
			}
			p = n
		}
	}
	if p == m.head {
		return nil
	}
	return p
}

func (m *MemTable) findGreaterOrEqual(ikey []byte, prevs []*node) (n *node, hit bool) {
	p := m.head
	for h := m.height - 1; h >= 0; h-- {
		n = p.Next(h)
		for {
			if n == nil {
				break
			}
			if r := m.icmp.Compare(ikey, n.ikey); r <= 0 {
				hit = r == 0
				break
			}
			p, n = n, n.Next(h)
		}
		prevs[h] = p
	}
	return
}

func (m *MemTable) ApproximateMemoryUsage() int {
	return m.usage
}

func (m *MemTable) Empty() bool {
	return m.head.Next(0) == nil
}

func (m *MemTable) Batch(seq keys.Sequence, items []batch.Item) {
	for _, item := range items {
		switch item.Value {
		case nil:
			m.Add(seq, keys.Delete, item.Key, nil)
		default:
			m.Add(seq, keys.Value, item.Key, item.Value)
		}
		seq++
	}
}

func (m *MemTable) Add(seq keys.Sequence, kind keys.Kind, key, value []byte) {
	b := m.allocBytes(len(key) + keys.TagLen + len(value))
	ikey := b[:len(key)+keys.TagLen]
	keys.MakeInternalKey(ikey, key, seq, kind)

	prevs := m.prevs[:]
	_, hit := m.findGreaterOrEqual(ikey, prevs)
	if hit {
		panic("duplicated key in MemTable")
	}

	n := m.allocNode()
	n.ikey = ikey
	n.value = b[len(ikey):]
	switch kind {
	case keys.Delete:
		n.value = nil
	default:
		copy(n.value, value)
	}

	h := m.randomHeight()
	n.nexts = m.allocNexts(h)

	if m.height < h {
		for i := m.height; i < h; i++ {
			prevs[i] = m.head
		}
		m.height = h
	}

	m.mutex.Lock()
	for i := 0; i < h; i++ {
		n.nexts[i] = prevs[i].nexts[i]
		prevs[i].SetNext(i, n)
	}
	m.mutex.Unlock()
}

func (m *MemTable) Get(ikey keys.InternalKey) (value []byte, err error, ok bool) {
	var prevs [maxHeight]*node
	m.mutex.RLock()
	n, hit := m.findGreaterOrEqual(ikey, prevs[:])
	m.mutex.RUnlock()
	if n != nil && (hit || m.icmp.UserKeyComparator.Compare(ikey.UserKey(), keys.InternalKey(n.ikey).UserKey()) == 0) {
		if n.value == nil {
			err = errors.ErrNotFound
		}
		return n.value, err, true
	}
	return nil, nil, false
}

func (m *MemTable) NewIterator() iterator.Iterator {
	return &memtableIterator{m: m}
}

func New(icmp *keys.InternalComparator) *MemTable {
	m := &MemTable{
		rnd:    rand.New(rand.NewSource(rand.Int63())),
		height: 1,
		icmp:   icmp,
	}
	m.head = m.newNode(maxHeight)
	return m
}
<|endoftext|>


<commit_before>// Copyright 2017 The Ebiten 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 restorable

import (
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

// restoringEnabled indicates if restoring happens or not.
//
// This value is overridden at enabled_*.go.
var restoringEnabled = true

// IsRestoringEnabled returns a boolean value indicating whether
// restoring process works or not.
func IsRestoringEnabled() bool {
	// This value is updated only at init or EnableRestoringForTesting.
	// No need to lock here.
	return restoringEnabled
}

// EnableRestoringForTesting forces to enable restoring for testing.
func EnableRestoringForTesting() {
	restoringEnabled = true
}

// images is a set of Image objects.
type images struct {
	images     map[*Image]struct{}
	lastTarget *Image
	m          sync.Mutex
}

// theImages represents the images for the current process.
var theImages = &images{
	images: map[*Image]struct{}{},
}

// ResolveStaleImages flushes the queued draw commands and resolves
// all stale images.
//
// ResolveStaleImages is intended to be called at the end of a frame.
func ResolveStaleImages() error {
	if err := graphics.FlushCommands(); err != nil {
		return err
	}
	if !restoringEnabled {
		return nil
	}
	return theImages.resolveStaleImages()
}

// Restore restores the images.
//
// Restoring means to make all *graphics.Image objects have their textures and framebuffers.
func Restore() error {
	if err := graphics.ResetGLState(); err != nil {
		return err
	}
	return theImages.restore()
}

// add adds img to the images.
func (i *images) add(img *Image) {
	i.m.Lock()
	defer i.m.Unlock()
	i.images[img] = struct{}{}
}

// remove removes img from the images.
func (i *images) remove(img *Image) {
	i.m.Lock()
	defer i.m.Unlock()
	delete(i.images, img)
}

// resolveStaleImages resolves stale images.
func (i *images) resolveStaleImages() error {
	i.m.Lock()
	defer i.m.Unlock()
	i.lastTarget = nil
	for img := range i.images {
		if err := img.resolveStale(); err != nil {
			return err
		}
	}
	return nil
}

// makeStaleIfDependingOn makes all the images stale that depend on target.
//
// When target is changed, all images depending on target can't be restored with target.
// makeStaleIfDependingOn is called in such situation.
func (i *images) makeStaleIfDependingOn(target *Image) {
	if target == nil {
		panic("not reached")
	}
	// Avoid defer for performance
	i.m.Lock()
	if i.lastTarget == target {
		i.m.Unlock()
		return
	}
	i.lastTarget = target
	for img := range i.images {
		// TODO: This seems not enough: What if img becomes stale but what about
		// other images depend on img? (#357)
		img.makeStaleIfDependingOn(target)
	}
	i.m.Unlock()
}

// restore restores the images.
//
// Restoring means to make all *graphics.Image objects have their textures and framebuffers.
func (i *images) restore() error {
	i.m.Lock()
	defer i.m.Unlock()
	if !IsRestoringEnabled() {
		panic("not reached")
	}

	// Framebuffers/textures cannot be disposed since framebuffers/textures that
	// don't belong to the current context.

	// Let's do topological sort based on dependencies of drawing history.
	// It is assured that there are not loops since cyclic drawing makes images stale.
	type edge struct {
		source *Image
		target *Image
	}
	images := map[*Image]struct{}{}
	for i := range i.images {
		images[i] = struct{}{}
	}
	edges := map[edge]struct{}{}
	for t := range images {
		for s := range t.dependingImages() {
			edges[edge{source: s, target: t}] = struct{}{}
		}
	}
	sorted := []*Image{}
	for len(images) > 0 {
		// current repesents images that have no incoming edges.
		current := map[*Image]struct{}{}
		for i := range images {
			current[i] = struct{}{}
		}
		for e := range edges {
			if _, ok := current[e.target]; ok {
				delete(current, e.target)
			}
		}
		for i := range current {
			delete(images, i)
			sorted = append(sorted, i)
		}
		removed := []edge{}
		for e := range edges {
			if _, ok := current[e.source]; ok {
				removed = append(removed, e)
			}
		}
		for _, e := range removed {
			delete(edges, e)
		}
	}
	for _, img := range sorted {
		if err := img.restore(); err != nil {
			return err
		}
	}
	return nil
}

// InitializeGLState initializes the GL state.
func InitializeGLState() error {
	return graphics.ResetGLState()
}
<commit_msg>restorable: Dispose images explicitly when restoring (#567)<commit_after>// Copyright 2017 The Ebiten 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 restorable

import (
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

// restoringEnabled indicates if restoring happens or not.
//
// This value is overridden at enabled_*.go.
var restoringEnabled = true

// IsRestoringEnabled returns a boolean value indicating whether
// restoring process works or not.
func IsRestoringEnabled() bool {
	// This value is updated only at init or EnableRestoringForTesting.
	// No need to lock here.
	return restoringEnabled
}

// EnableRestoringForTesting forces to enable restoring for testing.
func EnableRestoringForTesting() {
	restoringEnabled = true
}

// images is a set of Image objects.
type images struct {
	images     map[*Image]struct{}
	lastTarget *Image
	m          sync.Mutex
}

// theImages represents the images for the current process.
var theImages = &images{
	images: map[*Image]struct{}{},
}

// ResolveStaleImages flushes the queued draw commands and resolves
// all stale images.
//
// ResolveStaleImages is intended to be called at the end of a frame.
func ResolveStaleImages() error {
	if err := graphics.FlushCommands(); err != nil {
		return err
	}
	if !restoringEnabled {
		return nil
	}
	return theImages.resolveStaleImages()
}

// Restore restores the images.
//
// Restoring means to make all *graphics.Image objects have their textures and framebuffers.
func Restore() error {
	if err := graphics.ResetGLState(); err != nil {
		return err
	}
	return theImages.restore()
}

// add adds img to the images.
func (i *images) add(img *Image) {
	i.m.Lock()
	defer i.m.Unlock()
	i.images[img] = struct{}{}
}

// remove removes img from the images.
func (i *images) remove(img *Image) {
	i.m.Lock()
	defer i.m.Unlock()
	delete(i.images, img)
}

// resolveStaleImages resolves stale images.
func (i *images) resolveStaleImages() error {
	i.m.Lock()
	defer i.m.Unlock()
	i.lastTarget = nil
	for img := range i.images {
		if err := img.resolveStale(); err != nil {
			return err
		}
	}
	return nil
}

// makeStaleIfDependingOn makes all the images stale that depend on target.
//
// When target is changed, all images depending on target can't be restored with target.
// makeStaleIfDependingOn is called in such situation.
func (i *images) makeStaleIfDependingOn(target *Image) {
	if target == nil {
		panic("not reached")
	}
	// Avoid defer for performance
	i.m.Lock()
	if i.lastTarget == target {
		i.m.Unlock()
		return
	}
	i.lastTarget = target
	for img := range i.images {
		// TODO: This seems not enough: What if img becomes stale but what about
		// other images depend on img? (#357)
		img.makeStaleIfDependingOn(target)
	}
	i.m.Unlock()
}

// restore restores the images.
//
// Restoring means to make all *graphics.Image objects have their textures and framebuffers.
func (i *images) restore() error {
	i.m.Lock()
	defer i.m.Unlock()
	if !IsRestoringEnabled() {
		panic("not reached")
	}

	// Dispose image explicitly
	for img := range i.images {
		img.image.Dispose()
		// img.image can't be set nil here, or Size() panics when restoring.
	}

	// Let's do topological sort based on dependencies of drawing history.
	// It is assured that there are not loops since cyclic drawing makes images stale.
	type edge struct {
		source *Image
		target *Image
	}
	images := map[*Image]struct{}{}
	for i := range i.images {
		images[i] = struct{}{}
	}
	edges := map[edge]struct{}{}
	for t := range images {
		for s := range t.dependingImages() {
			edges[edge{source: s, target: t}] = struct{}{}
		}
	}
	sorted := []*Image{}
	for len(images) > 0 {
		// current repesents images that have no incoming edges.
		current := map[*Image]struct{}{}
		for i := range images {
			current[i] = struct{}{}
		}
		for e := range edges {
			if _, ok := current[e.target]; ok {
				delete(current, e.target)
			}
		}
		for i := range current {
			delete(images, i)
			sorted = append(sorted, i)
		}
		removed := []edge{}
		for e := range edges {
			if _, ok := current[e.source]; ok {
				removed = append(removed, e)
			}
		}
		for _, e := range removed {
			delete(edges, e)
		}
	}
	for _, img := range sorted {
		if err := img.restore(); err != nil {
			return err
		}
	}
	return nil
}

// InitializeGLState initializes the GL state.
func InitializeGLState() error {
	return graphics.ResetGLState()
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 js

import (
	"syscall/js"
	"time"

	"github.com/hajimehoshi/ebiten/v2/internal/devicescale"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
	"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
	"github.com/hajimehoshi/ebiten/v2/internal/restorable"
)

type UserInterface struct {
	runnableOnUnfocused bool
	vsync               bool
	running             bool
	initFocused         bool

	sizeChanged bool
	contextLost bool

	lastDeviceScaleFactor float64

	context driver.UIContext
	input   Input
}

var theUI = &UserInterface{
	runnableOnUnfocused: true,
	sizeChanged:         true,
	vsync:               true,
	initFocused:         true,
}

func init() {
	theUI.input.ui = theUI
}

func Get() *UserInterface {
	return theUI
}

var (
	window                = js.Global().Get("window")
	document              = js.Global().Get("document")
	canvas                js.Value
	requestAnimationFrame = js.Global().Get("requestAnimationFrame")
	setTimeout            = js.Global().Get("setTimeout")
)

func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
	return window.Get("innerWidth").Int(), window.Get("innerHeight").Int()
}

func (u *UserInterface) SetFullscreen(fullscreen bool) {
	// Do nothing
}

func (u *UserInterface) IsFullscreen() bool {
	return false
}

func (u *UserInterface) IsFocused() bool {
	return u.isFocused()
}

func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
	u.runnableOnUnfocused = runnableOnUnfocused
}

func (u *UserInterface) IsRunnableOnUnfocused() bool {
	return u.runnableOnUnfocused
}

func (u *UserInterface) SetVsyncEnabled(enabled bool) {
	u.vsync = enabled
}

func (u *UserInterface) IsVsyncEnabled() bool {
	return u.vsync
}

func (u *UserInterface) CursorMode() driver.CursorMode {
	if canvas.Get("style").Get("cursor").String() != "none" {
		return driver.CursorModeVisible
	}
	return driver.CursorModeHidden
}

func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
	var visible bool
	switch mode {
	case driver.CursorModeVisible:
		visible = true
	case driver.CursorModeHidden:
		visible = false
	default:
		return
	}

	if visible {
		canvas.Get("style").Set("cursor", "auto")
	} else {
		canvas.Get("style").Set("cursor", "none")
	}
}

func (u *UserInterface) DeviceScaleFactor() float64 {
	return devicescale.GetAt(0, 0)
}

func (u *UserInterface) updateSize() {
	a := u.DeviceScaleFactor()
	if u.lastDeviceScaleFactor != a {
		u.updateScreenSize()
	}
	u.lastDeviceScaleFactor = a

	if u.sizeChanged {
		u.sizeChanged = false
		if document.Truthy() {
			body := document.Get("body")
			bw := body.Get("clientWidth").Float()
			bh := body.Get("clientHeight").Float()
			u.context.Layout(bw, bh)
		} else {
			u.context.Layout(640, 480)
		}
	}
}

func (u *UserInterface) suspended() bool {
	if u.runnableOnUnfocused {
		return false
	}
	return !u.isFocused()
}

func (u *UserInterface) isFocused() bool {
	if !document.Call("hasFocus").Bool() {
		return false
	}
	if document.Get("hidden").Bool() {
		return false
	}
	return true
}

func (u *UserInterface) update() error {
	if u.suspended() {
		hooks.SuspendAudio()
		return nil
	}
	hooks.ResumeAudio()

	u.input.UpdateGamepads()
	u.updateSize()
	if err := u.context.Update(); err != nil {
		return err
	}
	return nil
}

func (u *UserInterface) loop(context driver.UIContext) <-chan error {
	u.context = context

	errCh := make(chan error)
	reqStopAudioCh := make(chan struct{})
	resStopAudioCh := make(chan struct{})

	var cf js.Func
	f := func() {
		if u.contextLost {
			requestAnimationFrame.Invoke(cf)
			return
		}

		if err := u.update(); err != nil {
			close(reqStopAudioCh)
			<-resStopAudioCh

			errCh <- err
			close(errCh)
			return
		}
		if u.vsync {
			requestAnimationFrame.Invoke(cf)
		} else {
			setTimeout.Invoke(cf, 0)
		}
	}

	// TODO: Should cf be released after the game ends?
	cf = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// f can be blocked but callbacks must not be blocked. Create a goroutine (#1161).
		go f()
		return nil
	})

	// Call f asyncly to be async since ch is used in f.
	go f()

	// Run another loop to watch suspended() as the above update function is never called when the tab is hidden.
	// To check the document's visiblity, visibilitychange event should usually be used. However, this event is
	// not reliable and sometimes it is not fired (#961). Then, watch the state regularly instead.
	go func() {
		defer close(resStopAudioCh)

		const interval = 100 * time.Millisecond
		t := time.NewTicker(interval)
		defer func() {
			t.Stop()

			// This is a dirty hack. (*time.Ticker).Stop() just marks the timer 'deleted' [1] and
			// something might run even after Stop. On Wasm, this causes an issue to execute Go program
			// even after finishing (#1027). Sleep for the interval time duration to ensure that
			// everything related to the timer is finished.
			//
			// [1] runtime.deltimer
			time.Sleep(interval)
		}()

		for {
			select {
			case <-t.C:
				if u.suspended() {
					hooks.SuspendAudio()
				} else {
					hooks.ResumeAudio()
				}
			case <-reqStopAudioCh:
				return
			}
		}
	}()

	return errCh
}

func init() {
	// docuemnt is undefined on node.js
	if !document.Truthy() {
		return
	}

	if !document.Get("body").Truthy() {
		ch := make(chan struct{})
		window.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			close(ch)
			return nil
		}))
		<-ch
	}

	window.Call("addEventListener", "resize", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		theUI.updateScreenSize()
		return nil
	}))

	// Adjust the initial scale to 1.
	// https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
	meta := document.Call("createElement", "meta")
	meta.Set("name", "viewport")
	meta.Set("content", "width=device-width, initial-scale=1")
	document.Get("head").Call("appendChild", meta)

	canvas = document.Call("createElement", "canvas")
	canvas.Set("width", 16)
	canvas.Set("height", 16)

	document.Get("body").Call("appendChild", canvas)

	htmlStyle := document.Get("documentElement").Get("style")
	htmlStyle.Set("height", "100%")
	htmlStyle.Set("margin", "0")
	htmlStyle.Set("padding", "0")

	bodyStyle := document.Get("body").Get("style")
	bodyStyle.Set("backgroundColor", "#000")
	bodyStyle.Set("height", "100%")
	bodyStyle.Set("margin", "0")
	bodyStyle.Set("padding", "0")

	canvasStyle := canvas.Get("style")
	canvasStyle.Set("width", "100%")
	canvasStyle.Set("height", "100%")
	canvasStyle.Set("margin", "0")
	canvasStyle.Set("padding", "0")

	// Make the canvas focusable.
	canvas.Call("setAttribute", "tabindex", 1)
	canvas.Get("style").Set("outline", "none")

	// Keyboard
	canvas.Call("addEventListener", "keydown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Focus the canvas explicitly to activate tha game (#961).
		canvas.Call("focus")

		e := args[0]
		// Don't 'preventDefault' on keydown events or keypress events wouldn't work (#715).
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "keypress", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "keyup", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))

	// Mouse
	canvas.Call("addEventListener", "mousedown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Focus the canvas explicitly to activate tha game (#961).
		canvas.Call("focus")

		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "mouseup", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "mousemove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "wheel", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))

	// Touch
	canvas.Call("addEventListener", "touchstart", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Focus the canvas explicitly to activate tha game (#961).
		canvas.Call("focus")

		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "touchend", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	canvas.Call("addEventListener", "touchmove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))

	// Gamepad
	window.Call("addEventListener", "gamepadconnected", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Do nothing.
		return nil
	}))

	canvas.Call("addEventListener", "contextmenu", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		return nil
	}))

	// Context
	canvas.Call("addEventListener", "webglcontextlost", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.contextLost = true
		restorable.OnContextLost()
		return nil
	}))
	canvas.Call("addEventListener", "webglcontextrestored", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		theUI.contextLost = false
		return nil
	}))
}

func (u *UserInterface) Run(context driver.UIContext) error {
	if u.initFocused && window.Truthy() {
		// Do not focus the canvas when the current document is in an iframe.
		// Otherwise, the parent page tries to focus the iframe on every loading, which is annoying (#1373).
		isInIframe := !jsutil.Equal(window.Get("location"), window.Get("parent").Get("location"))
		if !isInIframe {
			canvas.Call("focus")
		}
	}
	u.running = true
	return <-u.loop(context)
}

func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) {
	panic("js: RunWithoutMainLoop is not implemented")
}

func (u *UserInterface) updateScreenSize() {
	if document.Truthy() {
		body := document.Get("body")
		bw := int(body.Get("clientWidth").Float() * u.DeviceScaleFactor())
		bh := int(body.Get("clientHeight").Float() * u.DeviceScaleFactor())
		canvas.Set("width", bw)
		canvas.Set("height", bh)
	}
	u.sizeChanged = true
}

func (u *UserInterface) SetScreenTransparent(transparent bool) {
	if u.running {
		panic("js: SetScreenTransparent can't be called after the main loop starts")
	}

	bodyStyle := document.Get("body").Get("style")
	if transparent {
		bodyStyle.Set("backgroundColor", "transparent")
	} else {
		bodyStyle.Set("backgroundColor", "#000")
	}
}

func (u *UserInterface) IsScreenTransparent() bool {
	bodyStyle := document.Get("body").Get("style")
	return bodyStyle.Get("backgroundColor").String() == "transparent"
}

func (u *UserInterface) ResetForFrame() {
	u.updateSize()
	u.input.resetForFrame()
}

func (u *UserInterface) SetInitFocused(focused bool) {
	if u.running {
		panic("ui: SetInitFocused must be called before the main loop")
	}
	u.initFocused = focused
}

func (u *UserInterface) Input() driver.Input {
	return &u.input
}

func (u *UserInterface) Window() driver.Window {
	return nil
}

func (*UserInterface) Graphics() driver.Graphics {
	return opengl.Get()
}
<commit_msg>uidriver/js: Separate registering event handlers<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 js

import (
	"syscall/js"
	"time"

	"github.com/hajimehoshi/ebiten/v2/internal/devicescale"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
	"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
	"github.com/hajimehoshi/ebiten/v2/internal/restorable"
)

type UserInterface struct {
	runnableOnUnfocused bool
	vsync               bool
	running             bool
	initFocused         bool

	sizeChanged bool
	contextLost bool

	lastDeviceScaleFactor float64

	context driver.UIContext
	input   Input
}

var theUI = &UserInterface{
	runnableOnUnfocused: true,
	sizeChanged:         true,
	vsync:               true,
	initFocused:         true,
}

func init() {
	theUI.input.ui = theUI
}

func Get() *UserInterface {
	return theUI
}

var (
	window                = js.Global().Get("window")
	document              = js.Global().Get("document")
	canvas                js.Value
	requestAnimationFrame = js.Global().Get("requestAnimationFrame")
	setTimeout            = js.Global().Get("setTimeout")
)

func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
	return window.Get("innerWidth").Int(), window.Get("innerHeight").Int()
}

func (u *UserInterface) SetFullscreen(fullscreen bool) {
	// Do nothing
}

func (u *UserInterface) IsFullscreen() bool {
	return false
}

func (u *UserInterface) IsFocused() bool {
	return u.isFocused()
}

func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
	u.runnableOnUnfocused = runnableOnUnfocused
}

func (u *UserInterface) IsRunnableOnUnfocused() bool {
	return u.runnableOnUnfocused
}

func (u *UserInterface) SetVsyncEnabled(enabled bool) {
	u.vsync = enabled
}

func (u *UserInterface) IsVsyncEnabled() bool {
	return u.vsync
}

func (u *UserInterface) CursorMode() driver.CursorMode {
	if canvas.Get("style").Get("cursor").String() != "none" {
		return driver.CursorModeVisible
	}
	return driver.CursorModeHidden
}

func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
	var visible bool
	switch mode {
	case driver.CursorModeVisible:
		visible = true
	case driver.CursorModeHidden:
		visible = false
	default:
		return
	}

	if visible {
		canvas.Get("style").Set("cursor", "auto")
	} else {
		canvas.Get("style").Set("cursor", "none")
	}
}

func (u *UserInterface) DeviceScaleFactor() float64 {
	return devicescale.GetAt(0, 0)
}

func (u *UserInterface) updateSize() {
	a := u.DeviceScaleFactor()
	if u.lastDeviceScaleFactor != a {
		u.updateScreenSize()
	}
	u.lastDeviceScaleFactor = a

	if u.sizeChanged {
		u.sizeChanged = false
		if document.Truthy() {
			body := document.Get("body")
			bw := body.Get("clientWidth").Float()
			bh := body.Get("clientHeight").Float()
			u.context.Layout(bw, bh)
		} else {
			u.context.Layout(640, 480)
		}
	}
}

func (u *UserInterface) suspended() bool {
	if u.runnableOnUnfocused {
		return false
	}
	return !u.isFocused()
}

func (u *UserInterface) isFocused() bool {
	if !document.Call("hasFocus").Bool() {
		return false
	}
	if document.Get("hidden").Bool() {
		return false
	}
	return true
}

func (u *UserInterface) update() error {
	if u.suspended() {
		hooks.SuspendAudio()
		return nil
	}
	hooks.ResumeAudio()

	u.input.UpdateGamepads()
	u.updateSize()
	if err := u.context.Update(); err != nil {
		return err
	}
	return nil
}

func (u *UserInterface) loop(context driver.UIContext) <-chan error {
	u.context = context

	errCh := make(chan error)
	reqStopAudioCh := make(chan struct{})
	resStopAudioCh := make(chan struct{})

	var cf js.Func
	f := func() {
		if u.contextLost {
			requestAnimationFrame.Invoke(cf)
			return
		}

		if err := u.update(); err != nil {
			close(reqStopAudioCh)
			<-resStopAudioCh

			errCh <- err
			close(errCh)
			return
		}
		if u.vsync {
			requestAnimationFrame.Invoke(cf)
		} else {
			setTimeout.Invoke(cf, 0)
		}
	}

	// TODO: Should cf be released after the game ends?
	cf = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// f can be blocked but callbacks must not be blocked. Create a goroutine (#1161).
		go f()
		return nil
	})

	// Call f asyncly to be async since ch is used in f.
	go f()

	// Run another loop to watch suspended() as the above update function is never called when the tab is hidden.
	// To check the document's visiblity, visibilitychange event should usually be used. However, this event is
	// not reliable and sometimes it is not fired (#961). Then, watch the state regularly instead.
	go func() {
		defer close(resStopAudioCh)

		const interval = 100 * time.Millisecond
		t := time.NewTicker(interval)
		defer func() {
			t.Stop()

			// This is a dirty hack. (*time.Ticker).Stop() just marks the timer 'deleted' [1] and
			// something might run even after Stop. On Wasm, this causes an issue to execute Go program
			// even after finishing (#1027). Sleep for the interval time duration to ensure that
			// everything related to the timer is finished.
			//
			// [1] runtime.deltimer
			time.Sleep(interval)
		}()

		for {
			select {
			case <-t.C:
				if u.suspended() {
					hooks.SuspendAudio()
				} else {
					hooks.ResumeAudio()
				}
			case <-reqStopAudioCh:
				return
			}
		}
	}()

	return errCh
}

func init() {
	// docuemnt is undefined on node.js
	if !document.Truthy() {
		return
	}

	if !document.Get("body").Truthy() {
		ch := make(chan struct{})
		window.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			close(ch)
			return nil
		}))
		<-ch
	}

	setWindowEventHandlers(window)

	// Adjust the initial scale to 1.
	// https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
	meta := document.Call("createElement", "meta")
	meta.Set("name", "viewport")
	meta.Set("content", "width=device-width, initial-scale=1")
	document.Get("head").Call("appendChild", meta)

	canvas = document.Call("createElement", "canvas")
	canvas.Set("width", 16)
	canvas.Set("height", 16)

	document.Get("body").Call("appendChild", canvas)

	htmlStyle := document.Get("documentElement").Get("style")
	htmlStyle.Set("height", "100%")
	htmlStyle.Set("margin", "0")
	htmlStyle.Set("padding", "0")

	bodyStyle := document.Get("body").Get("style")
	bodyStyle.Set("backgroundColor", "#000")
	bodyStyle.Set("height", "100%")
	bodyStyle.Set("margin", "0")
	bodyStyle.Set("padding", "0")

	canvasStyle := canvas.Get("style")
	canvasStyle.Set("width", "100%")
	canvasStyle.Set("height", "100%")
	canvasStyle.Set("margin", "0")
	canvasStyle.Set("padding", "0")

	// Make the canvas focusable.
	canvas.Call("setAttribute", "tabindex", 1)
	canvas.Get("style").Set("outline", "none")

	setCanvasEventHandlers(canvas)
}

func setWindowEventHandlers(v js.Value) {
	v.Call("addEventListener", "resize", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		theUI.updateScreenSize()
		return nil
	}))

	v.Call("addEventListener", "gamepadconnected", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Do nothing.
		return nil
	}))
}

func setCanvasEventHandlers(v js.Value) {
	// Keyboard
	v.Call("addEventListener", "keydown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Focus the canvas explicitly to activate tha game (#961).
		v.Call("focus")

		e := args[0]
		// Don't 'preventDefault' on keydown events or keypress events wouldn't work (#715).
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "keypress", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "keyup", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))

	// Mouse
	v.Call("addEventListener", "mousedown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Focus the canvas explicitly to activate tha game (#961).
		v.Call("focus")

		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "mouseup", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "mousemove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "wheel", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))

	// Touch
	v.Call("addEventListener", "touchstart", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Focus the canvas explicitly to activate tha game (#961).
		v.Call("focus")

		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "touchend", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))
	v.Call("addEventListener", "touchmove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.input.Update(e)
		return nil
	}))

	// Gamepad
	v.Call("addEventListener", "contextmenu", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		return nil
	}))

	// Context
	v.Call("addEventListener", "webglcontextlost", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		e.Call("preventDefault")
		theUI.contextLost = true
		restorable.OnContextLost()
		return nil
	}))
	v.Call("addEventListener", "webglcontextrestored", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		theUI.contextLost = false
		return nil
	}))
}

func (u *UserInterface) Run(context driver.UIContext) error {
	if u.initFocused && window.Truthy() {
		// Do not focus the canvas when the current document is in an iframe.
		// Otherwise, the parent page tries to focus the iframe on every loading, which is annoying (#1373).
		isInIframe := !jsutil.Equal(window.Get("location"), window.Get("parent").Get("location"))
		if !isInIframe {
			canvas.Call("focus")
		}
	}
	u.running = true
	return <-u.loop(context)
}

func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) {
	panic("js: RunWithoutMainLoop is not implemented")
}

func (u *UserInterface) updateScreenSize() {
	if document.Truthy() {
		body := document.Get("body")
		bw := int(body.Get("clientWidth").Float() * u.DeviceScaleFactor())
		bh := int(body.Get("clientHeight").Float() * u.DeviceScaleFactor())
		canvas.Set("width", bw)
		canvas.Set("height", bh)
	}
	u.sizeChanged = true
}

func (u *UserInterface) SetScreenTransparent(transparent bool) {
	if u.running {
		panic("js: SetScreenTransparent can't be called after the main loop starts")
	}

	bodyStyle := document.Get("body").Get("style")
	if transparent {
		bodyStyle.Set("backgroundColor", "transparent")
	} else {
		bodyStyle.Set("backgroundColor", "#000")
	}
}

func (u *UserInterface) IsScreenTransparent() bool {
	bodyStyle := document.Get("body").Get("style")
	return bodyStyle.Get("backgroundColor").String() == "transparent"
}

func (u *UserInterface) ResetForFrame() {
	u.updateSize()
	u.input.resetForFrame()
}

func (u *UserInterface) SetInitFocused(focused bool) {
	if u.running {
		panic("ui: SetInitFocused must be called before the main loop")
	}
	u.initFocused = focused
}

func (u *UserInterface) Input() driver.Input {
	return &u.input
}

func (u *UserInterface) Window() driver.Window {
	return nil
}

func (*UserInterface) Graphics() driver.Graphics {
	return opengl.Get()
}
<|endoftext|>