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

// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"time"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
	net.Listener
	config *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *listener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
	l := new(listener)
	l.Listener = inner
	l.config = config
	return l
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
	if config == nil || (len(config.Certificates) == 0 && config.GetCertificate == nil) {
		return nil, errors.New("tls: neither Certificates nor GetCertificate set in Config")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of Config for the defaults.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
	// We want the Timeout and Deadline values from dialer to cover the
	// whole process: TCP connection and TLS handshake. This means that we
	// also need to start our own timers now.
	timeout := dialer.Timeout

	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}

	var errChannel chan error

	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- timeoutError{}
		})
	}

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	// If no ServerName is set, infer the ServerName
	// from the hostname we're connecting to.
	if config.ServerName == "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}

	conn := Client(rawConn, config)

	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()

		err = <-errChannel
	}

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
	return DialWithDialer(new(net.Dialer), network, addr, config)
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return Certificate{}, err
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return Certificate{}, err
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}

// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
	fail := func(err error) (Certificate, error) { return Certificate{}, err }

	var cert Certificate
	var skippedBlockTypes []string
	for {
		var certDERBlock *pem.Block
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		} else {
			skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
		}
	}

	if len(cert.Certificate) == 0 {
		if len(skippedBlockTypes) == 0 {
			return fail(errors.New("crypto/tls: failed to find any PEM data in certificate input"))
		} else if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
			return fail(errors.New("crypto/tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
		} else {
			return fail(fmt.Errorf("crypto/tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
		}
	}

	skippedBlockTypes = skippedBlockTypes[:0]
	var keyDERBlock *pem.Block
	for {
		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
		if keyDERBlock == nil {
			if len(skippedBlockTypes) == 0 {
				return fail(errors.New("crypto/tls: failed to find any PEM data in key input"))
			} else if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
				return fail(errors.New("crypto/tls: found a certificate rather than a key in the PEM for the private key"))
			} else {
				return fail(fmt.Errorf("crypto/tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
			}
		}
		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
			break
		}
		skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
	}

	var err error
	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
	if err != nil {
		return fail(err)
	}

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return fail(err)
	}

	switch pub := x509Cert.PublicKey.(type) {
	case *rsa.PublicKey:
		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))
		}
		if pub.N.Cmp(priv.N) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	case *ecdsa.PublicKey:
		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))

		}
		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	default:
		return fail(errors.New("crypto/tls: unknown public key algorithm"))
	}

	return cert, nil
}

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		case *rsa.PrivateKey, *ecdsa.PrivateKey:
			return key, nil
		default:
			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
		}
	}
	if key, err := x509.ParseECPrivateKey(der); err == nil {
		return key, nil
	}

	return nil, errors.New("crypto/tls: failed to parse private key")
}
<commit_msg>crypto/tls: document lack of Lucky13 hardening<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls

// BUG(agl): The crypto/tls package does not implement countermeasures
// against Lucky13 attacks on CBC-mode encryption. See
// http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"time"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
	net.Listener
	config *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *listener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
	l := new(listener)
	l.Listener = inner
	l.config = config
	return l
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
	if config == nil || (len(config.Certificates) == 0 && config.GetCertificate == nil) {
		return nil, errors.New("tls: neither Certificates nor GetCertificate set in Config")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of Config for the defaults.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
	// We want the Timeout and Deadline values from dialer to cover the
	// whole process: TCP connection and TLS handshake. This means that we
	// also need to start our own timers now.
	timeout := dialer.Timeout

	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}

	var errChannel chan error

	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- timeoutError{}
		})
	}

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	// If no ServerName is set, infer the ServerName
	// from the hostname we're connecting to.
	if config.ServerName == "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}

	conn := Client(rawConn, config)

	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()

		err = <-errChannel
	}

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
	return DialWithDialer(new(net.Dialer), network, addr, config)
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return Certificate{}, err
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return Certificate{}, err
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}

// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
	fail := func(err error) (Certificate, error) { return Certificate{}, err }

	var cert Certificate
	var skippedBlockTypes []string
	for {
		var certDERBlock *pem.Block
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		} else {
			skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
		}
	}

	if len(cert.Certificate) == 0 {
		if len(skippedBlockTypes) == 0 {
			return fail(errors.New("crypto/tls: failed to find any PEM data in certificate input"))
		} else if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
			return fail(errors.New("crypto/tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
		} else {
			return fail(fmt.Errorf("crypto/tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
		}
	}

	skippedBlockTypes = skippedBlockTypes[:0]
	var keyDERBlock *pem.Block
	for {
		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
		if keyDERBlock == nil {
			if len(skippedBlockTypes) == 0 {
				return fail(errors.New("crypto/tls: failed to find any PEM data in key input"))
			} else if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
				return fail(errors.New("crypto/tls: found a certificate rather than a key in the PEM for the private key"))
			} else {
				return fail(fmt.Errorf("crypto/tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
			}
		}
		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
			break
		}
		skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
	}

	var err error
	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
	if err != nil {
		return fail(err)
	}

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return fail(err)
	}

	switch pub := x509Cert.PublicKey.(type) {
	case *rsa.PublicKey:
		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))
		}
		if pub.N.Cmp(priv.N) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	case *ecdsa.PublicKey:
		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))

		}
		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	default:
		return fail(errors.New("crypto/tls: unknown public key algorithm"))
	}

	return cert, nil
}

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		case *rsa.PrivateKey, *ecdsa.PrivateKey:
			return key, nil
		default:
			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
		}
	}
	if key, err := x509.ParseECPrivateKey(der); err == nil {
		return key, nil
	}

	return nil, errors.New("crypto/tls: failed to parse private key")
}
<|endoftext|>


<commit_before>package docker_image_resource_test

import (
	"bytes"
	"os/exec"

	"encoding/json"
	"os"
	"time"

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

var _ = Describe("Out", func() {
	BeforeEach(func() {
		os.Setenv("PATH", "/docker-image-resource/tests/fixtures/bin:"+os.Getenv("PATH"))
		os.Setenv("SKIP_PRIVILEGED", "true")
		// os.Setenv("LOG_FILE", "/dev/stderr")
	})

	put := func(params map[string]interface{}) *gexec.Session {
		command := exec.Command("/opt/resource/out", "/tmp")

		resourceInput, err := json.Marshal(params)
		Expect(err).ToNot(HaveOccurred())

		command.Stdin = bytes.NewBuffer(resourceInput)

		session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
		Expect(err).ToNot(HaveOccurred())
		session.Wait(10 * time.Second)
		return session
	}

	docker := func(cmd string) string {
		return "DOCKER: " + cmd
	}

	dockerd := func(cmd string) string {
		return "DOCKERD: " + cmd
	}

	It("starts dockerd with --data-root under /scratch", func() {
		session := put(map[string]interface{}{
			"source": map[string]interface{}{
				"repository": "test",
			},
			"params": map[string]interface{}{
				"build": "/docker-image-resource/tests/fixtures/build",
			},
		})

		Expect(session.Err).To(gbytes.Say(dockerd(`.*--data-root /scratch/docker.*`)))
	})

	Context("when configured with a insecure registries", func() {
		It("passes them to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":          "test",
					"insecure_registries": []string{"my-registry.gov", "other-registry.biz"},
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--insecure-registry my-registry\.gov --insecure-registry other-registry\.biz.*`)))
		})
	})

	Context("when configured with a registry mirror", func() {
		It("passes it to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":      "test",
					"registry_mirror": "some-mirror",
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--registry-mirror some-mirror.*`)))
		})
	})

	Context("When using ECR", func() {
		It("calls docker pull with the ECR registry", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})

		It("calls docker pull for an ECR images in a multi build docker file", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile.multi",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})
	})
})
<commit_msg>remove arbitrary timeout for better debugging<commit_after>package docker_image_resource_test

import (
	"bytes"
	"os/exec"

	"encoding/json"
	"os"

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

var _ = Describe("Out", func() {
	BeforeEach(func() {
		os.Setenv("PATH", "/docker-image-resource/tests/fixtures/bin:"+os.Getenv("PATH"))
		os.Setenv("SKIP_PRIVILEGED", "true")
		// os.Setenv("LOG_FILE", "/dev/stderr")
	})

	put := func(params map[string]interface{}) *gexec.Session {
		command := exec.Command("/opt/resource/out", "/tmp")

		resourceInput, err := json.Marshal(params)
		Expect(err).ToNot(HaveOccurred())

		command.Stdin = bytes.NewBuffer(resourceInput)

		session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
		Expect(err).ToNot(HaveOccurred())
		<-session.Exited
		return session
	}

	docker := func(cmd string) string {
		return "DOCKER: " + cmd
	}

	dockerd := func(cmd string) string {
		return "DOCKERD: " + cmd
	}

	It("starts dockerd with --data-root under /scratch", func() {
		session := put(map[string]interface{}{
			"source": map[string]interface{}{
				"repository": "test",
			},
			"params": map[string]interface{}{
				"build": "/docker-image-resource/tests/fixtures/build",
			},
		})

		Expect(session.Err).To(gbytes.Say(dockerd(`.*--data-root /scratch/docker.*`)))
	})

	Context("when configured with a insecure registries", func() {
		It("passes them to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":          "test",
					"insecure_registries": []string{"my-registry.gov", "other-registry.biz"},
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--insecure-registry my-registry\.gov --insecure-registry other-registry\.biz.*`)))
		})
	})

	Context("when configured with a registry mirror", func() {
		It("passes it to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":      "test",
					"registry_mirror": "some-mirror",
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--registry-mirror some-mirror.*`)))
		})
	})

	Context("When using ECR", func() {
		It("calls docker pull with the ECR registry", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})

		It("calls docker pull for an ECR images in a multi build docker file", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile.multi",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})
	})
})
<|endoftext|>


<commit_before>package docker_image_resource_test

import (
	"bytes"
	"os/exec"

	"encoding/json"
	"os"
	"time"

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

var _ = Describe("Out", func() {
	BeforeEach(func() {
		os.Setenv("PATH", "/docker-image-resource/tests/fixtures/bin:"+os.Getenv("PATH"))
		os.Setenv("SKIP_PRIVILEGED", "true")
		os.Setenv("LOG_FILE", "/dev/stderr")
	})

	put := func(params map[string]interface{}) *gexec.Session {
		command := exec.Command("/opt/resource/out", "/tmp")

		resourceInput, err := json.Marshal(params)
		Expect(err).ToNot(HaveOccurred())

		command.Stdin = bytes.NewBuffer(resourceInput)

		session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
		Expect(err).ToNot(HaveOccurred())
		<-session.Exited
		return session
	}

	dockerarg := func(cmd string) string {
		return "DOCKER ARG: " + cmd
	}

	docker := func(cmd string) string {
		return "DOCKER: " + cmd
	}

	dockerd := func(cmd string) string {
		return "DOCKERD: " + cmd
	}

	It("starts dockerd with --data-root under /scratch", func() {
		session := put(map[string]interface{}{
			"source": map[string]interface{}{
				"repository": "test",
			},
			"params": map[string]interface{}{
				"build": "/docker-image-resource/tests/fixtures/build",
			},
		})

		Expect(session.Err).To(gbytes.Say(dockerd(`.*--data-root /scratch/docker.*`)))
	})

	Context("when configured with a insecure registries", func() {
		It("passes them to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":          "test",
					"insecure_registries": []string{"my-registry.gov", "other-registry.biz"},
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--insecure-registry my-registry\.gov --insecure-registry other-registry\.biz.*`)))
		})
	})

	Context("when configured with a registry mirror", func() {
		It("passes it to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":      "test",
					"registry_mirror": "some-mirror",
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--registry-mirror some-mirror.*`)))
		})
	})

	Context("When using ECR", func() {
		It("calls docker pull with the ECR registry", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})

		It("calls docker pull for an ECR images in a multi build docker file", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile.multi",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})
	})
})
<commit_msg>more debugging to find the hang<commit_after>package docker_image_resource_test

import (
	"bytes"
	"os/exec"

	"encoding/json"
	"os"
	"time"

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

var _ = Describe("Out", func() {
	BeforeEach(func() {
		os.Setenv("PATH", "/docker-image-resource/tests/fixtures/bin:"+os.Getenv("PATH"))
		os.Setenv("SKIP_PRIVILEGED", "true")
		// os.Setenv("LOG_FILE", "/dev/stderr")
	})

	put := func(params map[string]interface{}) *gexec.Session {
		command := exec.Command("/opt/resource/out", "/tmp")

		resourceInput, err := json.Marshal(params)
		Expect(err).ToNot(HaveOccurred())

		command.Stdin = bytes.NewBuffer(resourceInput)

		session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
		Expect(err).ToNot(HaveOccurred())
		<-session.Exited
		return session
	}

	dockerarg := func(cmd string) string {
		return "DOCKER ARG: " + cmd
	}

	docker := func(cmd string) string {
		return "DOCKER: " + cmd
	}

	dockerd := func(cmd string) string {
		return "DOCKERD: " + cmd
	}

	It("starts dockerd with --data-root under /scratch", func() {
		session := put(map[string]interface{}{
			"source": map[string]interface{}{
				"repository": "test",
			},
			"params": map[string]interface{}{
				"build": "/docker-image-resource/tests/fixtures/build",
			},
		})

		Expect(session.Err).To(gbytes.Say(dockerd(`.*--data-root /scratch/docker.*`)))
	})

	Context("when configured with a insecure registries", func() {
		It("passes them to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":          "test",
					"insecure_registries": []string{"my-registry.gov", "other-registry.biz"},
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--insecure-registry my-registry\.gov --insecure-registry other-registry\.biz.*`)))
		})
	})

	Context("when configured with a registry mirror", func() {
		It("passes it to dockerd", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository":      "test",
					"registry_mirror": "some-mirror",
				},
				"params": map[string]interface{}{
					"build": "/docker-image-resource/tests/fixtures/build",
				},
			})

			Expect(session.Err).To(gbytes.Say(dockerd(`.*--registry-mirror some-mirror.*`)))
		})
	})

	Context("When using ECR", func() {
		It("calls docker pull with the ECR registry", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})

		It("calls docker pull for an ECR images in a multi build docker file", func() {
			session := put(map[string]interface{}{
				"source": map[string]interface{}{
					"repository": "test",
				},
				"params": map[string]interface{}{
					"build":      "/docker-image-resource/tests/fixtures/ecr",
					"dockerfile": "/docker-image-resource/tests/fixtures/ecr/Dockerfile.multi",
				},
			})

			Expect(session.Err).To(gbytes.Say(docker("pull 123123.dkr.ecr.us-west-2.amazonaws.com:443/testing")))
		})
	})
})
<|endoftext|>


<commit_before>// Package diff implements line oriented diffs, similar to the ancient
// Unix diff command.
//
// The current implementation is just a wrapper around Sergi's
// go-diff/diffmatchpatch library, which is a go port of Neil
// Fraser's google-diff-match-patch code
package diff

import (
	"bytes"

	"github.com/sergi/go-diff/diffmatchpatch"
)

// Do computes the (line oriented) modifications needed to turn the src
// string into the dst string.
func Do(src, dst string) (diffs []diffmatchpatch.Diff) {
	dmp := diffmatchpatch.New()
	wSrc, wDst, warray := dmp.DiffLinesToChars(src, dst)
	diffs = dmp.DiffMain(wSrc, wDst, false)
	diffs = dmp.DiffCharsToLines(diffs, warray)
	return diffs
}

// Dst computes and returns the destination text.
func Dst(diffs []diffmatchpatch.Diff) string {
	var text bytes.Buffer
	for _, d := range diffs {
		if d.Type != diffmatchpatch.DiffDelete {
			text.WriteString(d.Text)
		}
	}
	return text.String()
}

// Src computes and returns the source text
func Src(diffs []diffmatchpatch.Diff) string {
	var text bytes.Buffer
	for _, d := range diffs {
		if d.Type != diffmatchpatch.DiffInsert {
			text.WriteString(d.Text)
		}
	}
	return text.String()
}
<commit_msg>utils: diff, skip useless rune->string conversion<commit_after>// Package diff implements line oriented diffs, similar to the ancient
// Unix diff command.
//
// The current implementation is just a wrapper around Sergi's
// go-diff/diffmatchpatch library, which is a go port of Neil
// Fraser's google-diff-match-patch code
package diff

import (
	"bytes"

	"github.com/sergi/go-diff/diffmatchpatch"
)

// Do computes the (line oriented) modifications needed to turn the src
// string into the dst string.
func Do(src, dst string) (diffs []diffmatchpatch.Diff) {
	dmp := diffmatchpatch.New()
	wSrc, wDst, warray := dmp.DiffLinesToRunes(src, dst)
	diffs = dmp.DiffMainRunes(wSrc, wDst, false)
	diffs = dmp.DiffCharsToLines(diffs, warray)
	return diffs
}

// Dst computes and returns the destination text.
func Dst(diffs []diffmatchpatch.Diff) string {
	var text bytes.Buffer
	for _, d := range diffs {
		if d.Type != diffmatchpatch.DiffDelete {
			text.WriteString(d.Text)
		}
	}
	return text.String()
}

// Src computes and returns the source text
func Src(diffs []diffmatchpatch.Diff) string {
	var text bytes.Buffer
	for _, d := range diffs {
		if d.Type != diffmatchpatch.DiffInsert {
			text.WriteString(d.Text)
		}
	}
	return text.String()
}
<|endoftext|>


<commit_before>/*
 * 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 file

import (
	"bufio"
	"bytes"
	"container/list"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	log "github.com/sirupsen/logrus"

	"fmt"

	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/paypal/dce-go/config"
	"github.com/paypal/dce-go/types"
	"github.com/paypal/dce-go/utils/pod"
	"golang.org/x/net/context"
	"gopkg.in/yaml.v2"
)

const (
	FILE_DELIMITER = ","
	YAML_SEPARATOR = "#---#"
	FILE_POSTFIX   = "-generated.yml"
	PATH_DELIMITER = "/"
	MAP_DELIMITER  = "="
)

type EditorFunc func(serviceName string, taskInfo *mesos.TaskInfo, executorId string, taskId string, containerDetails map[interface{}]interface{}, ports *list.Element) (map[interface{}]interface{}, *list.Element, error)

// Get required file list from label of fileName in taskInfo
func GetFiles(taskInfo *mesos.TaskInfo) ([]string, error) {
	log.Println("====================Retrieve  compose file list from fileName label====================")

	filelist := pod.GetLabel("fileName", taskInfo)
	if filelist == "" {
		err := errors.New("missing label fileName")
		log.Errorln(err)
		return nil, err
	}

	var files []string
	for _, file := range strings.Split(filelist, FILE_DELIMITER) {
		/*if strings.Contains(file, PATH_DELIMITER) {
			config.GetConfig().SetDefault(types.NO_FOLDER, true)
		}*/
		files = append(files, file)
	}

	log.Println("Required file list : ", files)
	return files, nil
}

// Get plugin order from label of pluginorder in taskInfo
func GetPluginOrder(taskInfo *mesos.TaskInfo) ([]string, error) {
	log.Println("====================Get plugin order====================")

	pluginList := pod.GetLabel(types.PLUGIN_ORDER, taskInfo)
	if pluginList == "" {
		err := errors.New("Missing label pluginorder")
		return nil, err
	}

	var plugins []string
	for _, plugin := range strings.Split(pluginList, FILE_DELIMITER) {
		plugins = append(plugins, plugin)
	}

	log.Println("Plugin Order : ", plugins)
	return plugins, nil
}

// Get downloaded file paths from uris
func GetYAML(taskInfo *mesos.TaskInfo) []string {
	log.Println("====================Get compose file from URI====================")
	var files []string
	uris := taskInfo.Executor.Command.GetUris()
	for _, uri := range uris {
		arr := strings.Split(uri.GetValue(), "/")
		name := arr[len(arr)-1]
		GetDirFilesRecv(name, &files)
	}
	log.Println("Compose file from URI(Pre_files): ", files)
	return files
}

// Get path compose file
// Since user may upload a tar ball which including all the compose files.
// In case they have different depth of folders to keep compose files, GetDirFilesRecv help to get the complete path of
// compose file
func GetDirFilesRecv(dir string, files *[]string) {
	if d, _ := os.Stat(dir); !d.IsDir() && (strings.Contains(dir, ".yml") ||
		strings.Contains(dir, ".yaml") && (!strings.Contains(dir, "config") ||
			!strings.Contains(dir, "plugin")) || dir == "yaml") {
		*files = append(*files, dir)
		return
	}
	dirs, err := ioutil.ReadDir(dir)
	if err != nil || dirs == nil || len(dirs) == 0 {
		log.Printf("%s is not a directory : %v", dir, err)
		return
	}
	for _, f := range dirs {
		if !f.IsDir() {
			if strings.Contains(f.Name(), ".yml") {
				*files = append(*files, dir+"/"+f.Name())
			}
		} else {
			GetDirFilesRecv(dir+"/"+f.Name(), files)
		}
	}
}

// search a file
// return path of a file
func SearchFile(root, file string) string {
	var filePath string
	filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
		if f.Name() == file {
			filePath = path
			return nil
		}
		return nil
	})
	return filePath
}

// check if "first" is subset of "second"
func IsSubset(first, second []string) bool {
	log.Println("subset : ", first)
	log.Println("set : ", second)
	set := make(map[string]int)
	for _, value := range second {
		set[value] += 1
	}

	for _, value := range first {
		if count, found := set[value]; !found {
			return false
		} else if count < 1 {
			return false
		} else {
			set[value] = count - 1
		}
	}

	return true
}

// check if file exist
func CheckFileExist(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		log.Println("File does not exit")
		return false
	}
	return true
}

// Add taskId as prefix
func PrefixTaskId(taskId string, session string) string {
	if strings.HasPrefix(session, taskId) {
		return session
	}
	return taskId + "_" + session
}

// Generate file with provided name and write data into it.
func WriteToFile(file string, data []byte) (string, error) {
	if !strings.Contains(file, config.GetAppFolder()) {
		file = FolderPath(strings.Fields(file))[0]
	}

	f, err := os.Create(file)
	if err != nil {
		log.Errorf("Error creating file %v", err)
		return "", err
	}

	_, err = f.Write(data)
	if err != nil {
		log.Errorln("Error writing into file : ", err.Error())
		return "", err
	}
	return f.Name(), nil
}

func WriteChangeToFiles(ctx context.Context) error {
	filesMap := ctx.Value(types.SERVICE_DETAIL).(types.ServiceDetail)
	for file := range filesMap {
		content, _ := yaml.Marshal(filesMap[file])
		_, err := WriteToFile(file.(string), content)
		if err != nil {
			return err
		}
	}
	return nil
}

func OverwriteFile(file string, data []byte) {
	os.Remove(file)

	f, err := os.Create(file)
	if err != nil {
		log.Errorln("Error creating file")
	}

	_, err = f.Write(data)
	if err != nil {
		log.Errorln("Error writing into file : ", err.Error())
	}
}

//Split a large file into a number of smaller files by file separator
func SplitYAML(file string) ([]string, error) {
	var names []string
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return nil, err
	}

	dat, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(strings.NewReader(string(dat)))
	scanner.Split(SplitFunc)
	for scanner.Scan() {
		splitData := scanner.Text()
		name := strings.TrimLeft(getYAMLDocumentName(splitData, "#.*yml"), "#")
		if name == "" {
			continue
		}
		fileName, err := WriteToFile(name, []byte(splitData))
		if err != nil {
			return nil, err
		}
		names = append(names, fileName)
	}

	if len(names) == 0 {
		names = append(names, file)
	}
	return names, nil
}

// splitYAMLDocument is a bufio.SplitFunc for splitting YAML streams into individual documents.
// This SplitFunc code is from K8s utils, since the yaml serperator is different, it can't be reused directly.
func SplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	sep := len([]byte(YAML_SEPARATOR))
	if i := bytes.Index(data, []byte(YAML_SEPARATOR)); i >= 0 {
		// We have a potential document terminator
		i += sep
		after := data[i:]
		if len(after) == 0 {
			// we can't read any more characters
			if atEOF {
				return len(data), data[:len(data)-sep], nil
			}
			return 0, nil, nil
		}
		if j := bytes.IndexByte(after, '\n'); j >= 0 {
			return i + j + 1, data[0 : i-sep], nil
		}
		return 0, nil, nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}
	// Request more data.
	return 0, nil, nil
}

// get yaml file name from a file using a defined pattern
func getYAMLDocumentName(data, pattern string) string {
	if len(data) <= 0 {
		return ""
	}
	r, _ := regexp.Compile(pattern)
	name := r.FindString(data)
	return name
}

// replace element in array
func ReplaceArrayElement(array interface{}, old string, new string) interface{} {
	if _array, ok := array.([]interface{}); ok {
		index, err := IndexArray(_array, old)
		if err != nil {
			return _array
		}
		_array[index] = new
		return _array

	}

	if _array, ok := array.(map[interface{}]interface{}); ok {
		_, exit := _array[old]
		if exit {
			_array[old] = new
		}
		return _array

	}

	return array
}

// get index of an element in array
func IndexArray(array []interface{}, element string) (int, error) {
	for i, e := range array {
		if e == element {
			return i, nil
		}
	}
	return -1, errors.New("Element missing in list")
}

func SearchInArray(array []interface{}, key string) string {
	for _, e := range array {
		if s := strings.Split(e.(string), MAP_DELIMITER); len(s) > 1 && s[0] == key {
			return s[1]
		}
	}
	return ""
}

// []string to []interface{}
func FormatInterfaceArray(s []string) []interface{} {
	t := make([]interface{}, len(s))
	for i, v := range s {
		t[i] = v
	}
	return t
}

// generate directories
func GenerateFileDirs(paths []string) error {
	log.Println("Generate Folders (0777): ", paths)
	for _, path := range paths {

		err := os.MkdirAll(path, 0777)
		if err != nil {
			log.Println("Error creating directory : ", err.Error())
			return err
		}

		os.Chmod(path, 0777)
	}
	return nil
}

func FolderPath(filenames []string) []string {
	if config.GetConfig().GetBool(types.NO_FOLDER) {
		return filenames
	}

	folder := config.GetAppFolder()

	for i, filename := range filenames {
		if !strings.Contains(filename, config.GetAppFolder()) {
			filenames[i] = strings.TrimSpace(folder) + PATH_DELIMITER + filename
		}
	}

	return filenames
}

func DeFolderPath(filepaths []string) []string {
	filenames := make([]string, len(filepaths))
	for _, file := range filepaths {
		filenames = append(filenames, filepath.Base(file))
	}
	return filenames
}

func ParseYamls(files []string) (map[interface{}](map[interface{}]interface{}), error) {
	res := make(map[interface{}](map[interface{}]interface{}))
	for _, file := range files {
		data, err := ioutil.ReadFile(file)
		if err != nil {
			log.Errorf("Error reading file %s : %v", file, err)
			return nil, err
		}
		m := make(map[interface{}]interface{})
		err = yaml.Unmarshal(data, &m)
		if err != nil {
			log.Errorf("Error unmarshalling %v", err)
		}
		res[FolderPath(strings.Fields(file))[0]] = m
	}
	return res, nil
}

// app folder is used to keep all the generated yml files
func GenerateAppFolder() error {
	// if app comes with a folder, then skip creating app folder
	/*if config.GetConfig().GetBool(types.NO_FOLDER) {
		return nil
	}*/

	folder := config.GetAppFolder()
	if folder == "" {
		folder = types.DEFAULT_FOLDER
		config.SetConfig(config.FOLDER_NAME, types.DEFAULT_FOLDER)
	}

	path, _ := filepath.Abs("")
	dirs := strings.Split(path, PATH_DELIMITER)
	folder = strings.TrimSpace(fmt.Sprintf("%s_%s", folder, dirs[len(dirs)-1]))

	_folder := []string{strings.TrimSpace(folder)}
	err := GenerateFileDirs(_folder)
	if err != nil {
		log.Println("Error generating file dirs: ", err.Error())
		return err
	}

	config.GetConfig().Set(config.FOLDER_NAME, folder)

	// copy compose files into pod folder
	for i, file := range pod.ComposeFiles {
		path := strings.Split(file, PATH_DELIMITER)
		dest := FolderPath(strings.Fields(path[len(path)-1]))[0]
		err = CopyFile(file, dest)
		if err != nil {
			log.Errorf("Failed to copy file into pod folder %v", err)
			return err
		}
		pod.ComposeFiles[i] = dest
	}
	return nil
}

func CopyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}

	return
}
<commit_msg>Get resource yaml file names from cmd uri<commit_after>/*
 * 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 file

import (
	"bufio"
	"bytes"
	"container/list"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	yaml "gopkg.in/yaml.v2"

	log "github.com/sirupsen/logrus"

	"fmt"

	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/paypal/dce-go/config"
	"github.com/paypal/dce-go/types"
	"github.com/paypal/dce-go/utils/pod"
	"golang.org/x/net/context"
)

const (
	FILE_DELIMITER = ","
	YAML_SEPARATOR = "#---#"
	FILE_POSTFIX   = "-generated.yml"
	PATH_DELIMITER = "/"
	MAP_DELIMITER  = "="
)

var ResourceYaml []string

type EditorFunc func(serviceName string, taskInfo *mesos.TaskInfo, executorId string, taskId string, containerDetails map[interface{}]interface{}, ports *list.Element) (map[interface{}]interface{}, *list.Element, error)

// Get required file list from label of fileName in taskInfo
func GetFiles(taskInfo *mesos.TaskInfo) ([]string, error) {
	log.Println("====================Retrieve  compose file list from fileName label====================")

	// Get download yml file name
	ResourceYaml = GetYAML(taskInfo)

	filelist := pod.GetLabel("fileName", taskInfo)
	if filelist == "" {
		err := errors.New("missing label fileName")
		log.Errorln(err)
		return nil, err
	}

	var files []string
	for _, file := range strings.Split(filelist, FILE_DELIMITER) {
		/*if strings.Contains(file, PATH_DELIMITER) {
			config.GetConfig().SetDefault(types.NO_FOLDER, true)
		}*/
		files = append(files, file)
	}

	log.Println("Required file list : ", files)
	return files, nil
}

// Get plugin order from label of pluginorder in taskInfo
func GetPluginOrder(taskInfo *mesos.TaskInfo) ([]string, error) {
	log.Println("====================Get plugin order====================")

	pluginList := pod.GetLabel(types.PLUGIN_ORDER, taskInfo)
	if pluginList == "" {
		err := errors.New("Missing label pluginorder")
		return nil, err
	}

	var plugins []string
	for _, plugin := range strings.Split(pluginList, FILE_DELIMITER) {
		plugins = append(plugins, plugin)
	}

	log.Println("Plugin Order : ", plugins)
	return plugins, nil
}

// Get downloaded file paths from uris
func GetYAML(taskInfo *mesos.TaskInfo) []string {
	log.Println("====================Get compose file from URI====================")
	var files []string
	uris := taskInfo.Executor.Command.GetUris()
	for _, uri := range uris {
		arr := strings.Split(uri.GetValue(), "/")
		name := arr[len(arr)-1]
		/*if strings.HasSuffix(name, ".yml") || name == "yaml" {
			log.Printf("Get yml file name from uri : %s", name)
		}*/
		GetDirFilesRecv(name, &files)
	}
	log.Println("Compose file from URI(Pre_files): ", files)
	return files
}

// Get path compose file
// Since user may upload a tar ball which including all the compose files.
// In case they have different depth of folders to keep compose files, GetDirFilesRecv help to get the complete path of
// compose file
func GetDirFilesRecv(dir string, files *[]string) {
	if d, _ := os.Stat(dir); !d.IsDir() && (strings.Contains(dir, ".yml") || dir == "yaml") {
		*files = append(*files, dir)
		return
	}
	dirs, err := ioutil.ReadDir(dir)
	if err != nil || dirs == nil || len(dirs) == 0 {
		log.Printf("%s is not a directory : %v", dir, err)
		return
	}
	for _, f := range dirs {
		if !f.IsDir() {
			if strings.Contains(f.Name(), ".yml") {
				*files = append(*files, dir+"/"+f.Name())
			}
		} else {
			GetDirFilesRecv(dir+"/"+f.Name(), files)
		}
	}
}

// search a file
// return path of a file
func SearchFile(root, file string) string {
	var filePath string
	filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
		if f.Name() == file {
			filePath = path
			return nil
		}
		return nil
	})
	return filePath
}

// check if "first" is subset of "second"
func IsSubset(first, second []string) bool {
	log.Println("subset : ", first)
	log.Println("set : ", second)
	set := make(map[string]int)
	for _, value := range second {
		set[value] += 1
	}

	for _, value := range first {
		if count, found := set[value]; !found {
			return false
		} else if count < 1 {
			return false
		} else {
			set[value] = count - 1
		}
	}

	return true
}

// check if file exist
func CheckFileExist(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		log.Println("File does not exit")
		return false
	}
	return true
}

// Add taskId as prefix
func PrefixTaskId(taskId string, session string) string {
	if strings.HasPrefix(session, taskId) {
		return session
	}
	return taskId + "_" + session
}

// Generate file with provided name and write data into it.
func WriteToFile(file string, data []byte) (string, error) {
	if !strings.Contains(file, config.GetAppFolder()) {
		file = FolderPath(strings.Fields(file))[0]
	}

	f, err := os.Create(file)
	if err != nil {
		log.Errorf("Error creating file %v", err)
		return "", err
	}

	_, err = f.Write(data)
	if err != nil {
		log.Errorln("Error writing into file : ", err.Error())
		return "", err
	}
	return f.Name(), nil
}

func WriteChangeToFiles(ctx context.Context) error {
	filesMap := ctx.Value(types.SERVICE_DETAIL).(types.ServiceDetail)
	for file := range filesMap {
		content, _ := yaml.Marshal(filesMap[file])
		_, err := WriteToFile(file.(string), content)
		if err != nil {
			return err
		}
	}
	return nil
}

func OverwriteFile(file string, data []byte) {
	os.Remove(file)

	f, err := os.Create(file)
	if err != nil {
		log.Errorln("Error creating file")
	}

	_, err = f.Write(data)
	if err != nil {
		log.Errorln("Error writing into file : ", err.Error())
	}
}

//Split a large file into a number of smaller files by file separator
func SplitYAML(file string) ([]string, error) {
	var names []string
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return nil, err
	}

	dat, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(strings.NewReader(string(dat)))
	scanner.Split(SplitFunc)
	for scanner.Scan() {
		splitData := scanner.Text()
		name := strings.TrimLeft(getYAMLDocumentName(splitData, "#.*yml"), "#")
		if name == "" {
			continue
		}
		fileName, err := WriteToFile(name, []byte(splitData))
		if err != nil {
			return nil, err
		}
		names = append(names, fileName)
	}

	if len(names) == 0 {
		names = append(names, file)
	}
	return names, nil
}

// splitYAMLDocument is a bufio.SplitFunc for splitting YAML streams into individual documents.
// This SplitFunc code is from K8s utils, since the yaml serperator is different, it can't be reused directly.
func SplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	sep := len([]byte(YAML_SEPARATOR))
	if i := bytes.Index(data, []byte(YAML_SEPARATOR)); i >= 0 {
		// We have a potential document terminator
		i += sep
		after := data[i:]
		if len(after) == 0 {
			// we can't read any more characters
			if atEOF {
				return len(data), data[:len(data)-sep], nil
			}
			return 0, nil, nil
		}
		if j := bytes.IndexByte(after, '\n'); j >= 0 {
			return i + j + 1, data[0 : i-sep], nil
		}
		return 0, nil, nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}
	// Request more data.
	return 0, nil, nil
}

// get yaml file name from a file using a defined pattern
func getYAMLDocumentName(data, pattern string) string {
	if len(data) <= 0 {
		return ""
	}
	r, _ := regexp.Compile(pattern)
	name := r.FindString(data)
	return name
}

// replace element in array
func ReplaceArrayElement(array interface{}, old string, new string) interface{} {
	if _array, ok := array.([]interface{}); ok {
		index, err := IndexArray(_array, old)
		if err != nil {
			return _array
		}
		_array[index] = new
		return _array

	}

	if _array, ok := array.(map[interface{}]interface{}); ok {
		_, exit := _array[old]
		if exit {
			_array[old] = new
		}
		return _array

	}

	return array
}

// get index of an element in array
func IndexArray(array []interface{}, element string) (int, error) {
	for i, e := range array {
		if e == element {
			return i, nil
		}
	}
	return -1, errors.New("Element missing in list")
}

func SearchInArray(array []interface{}, key string) string {
	for _, e := range array {
		if s := strings.Split(e.(string), MAP_DELIMITER); len(s) > 1 && s[0] == key {
			return s[1]
		}
	}
	return ""
}

// []string to []interface{}
func FormatInterfaceArray(s []string) []interface{} {
	t := make([]interface{}, len(s))
	for i, v := range s {
		t[i] = v
	}
	return t
}

// generate directories
func GenerateFileDirs(paths []string) error {
	log.Println("Generate Folders (0777): ", paths)
	for _, path := range paths {

		err := os.MkdirAll(path, 0777)
		if err != nil {
			log.Println("Error creating directory : ", err.Error())
			return err
		}

		os.Chmod(path, 0777)
	}
	return nil
}

func FolderPath(filenames []string) []string {
	if config.GetConfig().GetBool(types.NO_FOLDER) {
		return filenames
	}

	folder := config.GetAppFolder()

	for i, filename := range filenames {
		if !strings.Contains(filename, config.GetAppFolder()) {
			filenames[i] = strings.TrimSpace(folder) + PATH_DELIMITER + filename
		}
	}

	return filenames
}

func DeFolderPath(filepaths []string) []string {
	filenames := make([]string, len(filepaths))
	for _, file := range filepaths {
		filenames = append(filenames, filepath.Base(file))
	}
	return filenames
}

func ParseYamls(files []string) (map[interface{}](map[interface{}]interface{}), error) {
	res := make(map[interface{}](map[interface{}]interface{}))
	for _, file := range files {
		data, err := ioutil.ReadFile(file)
		if err != nil {
			log.Errorf("Error reading file %s : %v", file, err)
			return nil, err
		}
		m := make(map[interface{}]interface{})
		err = yaml.Unmarshal(data, &m)
		if err != nil {
			log.Errorf("Error unmarshalling %v", err)
		}
		res[FolderPath(strings.Fields(file))[0]] = m
	}
	return res, nil
}

// app folder is used to keep all the generated yml files
func GenerateAppFolder() error {
	// if app comes with a folder, then skip creating app folder
	/*if config.GetConfig().GetBool(types.NO_FOLDER) {
		return nil
	}*/

	folder := config.GetAppFolder()
	if folder == "" {
		folder = types.DEFAULT_FOLDER
		config.SetConfig(config.FOLDER_NAME, types.DEFAULT_FOLDER)
	}

	path, _ := filepath.Abs("")
	dirs := strings.Split(path, PATH_DELIMITER)
	folder = strings.TrimSpace(fmt.Sprintf("%s_%s", folder, dirs[len(dirs)-1]))

	_folder := []string{strings.TrimSpace(folder)}
	err := GenerateFileDirs(_folder)
	if err != nil {
		log.Println("Error generating file dirs: ", err.Error())
		return err
	}

	config.GetConfig().Set(config.FOLDER_NAME, folder)

	// copy compose files into pod folder
	/*for i, file := range pod.ComposeFiles {
		path := strings.Split(file, PATH_DELIMITER)
		dest := FolderPath(strings.Fields(path[len(path)-1]))[0]
		err = CopyFile(file, dest)
		if err != nil {
			log.Errorf("Failed to copy file into pod folder %v", err)
			return err
		}
		pod.ComposeFiles[i] = dest
	}*/
	return nil
}

func CopyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}

	return
}
<|endoftext|>


<commit_before>package main

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

func ParseDetail(inp io.Reader) map[string]string {
	var key string

	data := make(map[string]string)

	s := bufio.NewScanner(inp)
	for s.Scan() {
		line := s.Text()
		if len(strings.Trim(line, " ")) == 0 {
			continue
		}

		if strings.HasPrefix(line, " ") {
			data[key] = fmt.Sprintf("%s\n%s", data[key], strings.Trim(line, " "))

		} else {
			p := strings.SplitN(line, ":", 2)
			key = p[0]
			data[key] = strings.Trim(p[1], " ")
		}
	}

	return data
}

type PackageUrl struct {
	Url  string
	Name string
	Size string
	Hash string
}

type InstallInfo struct {
	Packages map[int][]string
	Urls     []PackageUrl
}

const (
	GROUP_NONE        = 0
	GROUP_EXTRA       = 1
	GROUP_SUGGESTED   = 2
	GROUP_RECOMMENDED = 3
	GROUP_INSTALL     = 4
	GROUP_UPGRADE     = 5
)

func ParseInstall(inp io.Reader) InstallInfo {
	s := bufio.NewScanner(inp)

	ii := InstallInfo{}
	ii.Packages = make(map[int][]string)
	ii.Urls = make([]PackageUrl, 0)

	group := GROUP_NONE
	more := s.Scan()
	for more {
		line := s.Text()
		if len(strings.Trim(line, " ")) == 0 {
			continue
		}

		fmt.Printf(">%s<\n", line)
		if strings.Contains(line, "The following extra packages will be installed:") {
			group = GROUP_EXTRA

		} else if strings.Contains(line, "Suggested packages:") {
			group = GROUP_SUGGESTED

		} else if strings.Contains(line, "Recommended packages:") {
			group = GROUP_RECOMMENDED

		} else if strings.Contains(line, "The following NEW packages will be installed:") {
			group = GROUP_INSTALL

		} else if strings.Contains(line, "The following packages will be upgraded:") {
			group = GROUP_UPGRADE

		} else if strings.Contains(line, "After this operation") {
			more = s.Scan()
			for more {
				line = s.Text()
				if len(strings.Trim(line, " ")) == 0 {
					more = s.Scan()
					break
				}

				p := strings.Split(line, " ")
				pu := PackageUrl{
					Url:  strings.Trim(p[0], "'"),
					Name: p[1],
					Size: p[2],
					Hash: p[3],
				}
				ii.Urls = append(ii.Urls, pu)

				more = s.Scan()
			}

		} else {
			more = s.Scan()
			group = GROUP_NONE
		}

		if group != GROUP_NONE {
			names := make([]string, 0)
			for {
				more = s.Scan()
				line = s.Text()
				if !strings.HasPrefix(line, " ") {
					break
				}

				p := strings.Split(strings.Trim(line, " "), " ")
				names = append(names, p...)
			}
			ii.Packages[group] = names
			group = GROUP_NONE
		}

	}

	return ii
}
<commit_msg>Remove debugging call<commit_after>package main

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

func ParseDetail(inp io.Reader) map[string]string {
	var key string

	data := make(map[string]string)

	s := bufio.NewScanner(inp)
	for s.Scan() {
		line := s.Text()
		if len(strings.Trim(line, " ")) == 0 {
			continue
		}

		if strings.HasPrefix(line, " ") {
			data[key] = fmt.Sprintf("%s\n%s", data[key], strings.Trim(line, " "))

		} else {
			p := strings.SplitN(line, ":", 2)
			key = p[0]
			data[key] = strings.Trim(p[1], " ")
		}
	}

	return data
}

type PackageUrl struct {
	Url  string
	Name string
	Size string
	Hash string
}

type InstallInfo struct {
	Packages map[int][]string
	Urls     []PackageUrl
}

const (
	GROUP_NONE        = 0
	GROUP_EXTRA       = 1
	GROUP_SUGGESTED   = 2
	GROUP_RECOMMENDED = 3
	GROUP_INSTALL     = 4
	GROUP_UPGRADE     = 5
)

func ParseInstall(inp io.Reader) InstallInfo {
	s := bufio.NewScanner(inp)

	ii := InstallInfo{}
	ii.Packages = make(map[int][]string)
	ii.Urls = make([]PackageUrl, 0)

	group := GROUP_NONE
	more := s.Scan()
	for more {
		line := s.Text()
		if len(strings.Trim(line, " ")) == 0 {
			continue
		}

		if strings.Contains(line, "The following extra packages will be installed:") {
			group = GROUP_EXTRA

		} else if strings.Contains(line, "Suggested packages:") {
			group = GROUP_SUGGESTED

		} else if strings.Contains(line, "Recommended packages:") {
			group = GROUP_RECOMMENDED

		} else if strings.Contains(line, "The following NEW packages will be installed:") {
			group = GROUP_INSTALL

		} else if strings.Contains(line, "The following packages will be upgraded:") {
			group = GROUP_UPGRADE

		} else if strings.Contains(line, "After this operation") {
			more = s.Scan()
			for more {
				line = s.Text()
				if len(strings.Trim(line, " ")) == 0 {
					more = s.Scan()
					break
				}

				p := strings.Split(line, " ")
				pu := PackageUrl{
					Url:  strings.Trim(p[0], "'"),
					Name: p[1],
					Size: p[2],
					Hash: p[3],
				}
				ii.Urls = append(ii.Urls, pu)

				more = s.Scan()
			}

		} else {
			more = s.Scan()
			group = GROUP_NONE
		}

		if group != GROUP_NONE {
			names := make([]string, 0)
			for {
				more = s.Scan()
				line = s.Text()
				if !strings.HasPrefix(line, " ") {
					break
				}

				p := strings.Split(strings.Trim(line, " "), " ")
				names = append(names, p...)
			}
			ii.Packages[group] = names
			group = GROUP_NONE
		}

	}

	return ii
}
<|endoftext|>


<commit_before>package textutil

import (
	"bytes"
	"fmt"
	"io"
)

const (
	borderCross = "+"
	borderRow   = "-"
	borderCol   = "|"
)

// Table represents a string-matrix
type Table interface {
	RowCount() int
	ColCount() int
	Get(i, j int) string
}

type CellStyleFunc func(row, col int, cell string) string

// WriteTable formats table to writer
func WriteTable(w io.Writer, table Table, styles ...CellStyleFunc) {
	rowCount, colCount := table.RowCount(), table.ColCount()
	if rowCount <= 0 || colCount <= 0 {
		return
	}
	widthArray := make([]int, colCount)
	for j := 0; j < colCount; j++ {
		maxWidth := 0
		for i := 0; i < rowCount; i++ {
			width := len(table.Get(i, j))
			if i == 0 || width > maxWidth {
				maxWidth = width
			}
		}
		widthArray[j] = maxWidth
	}
	rowBorder := rowBorderLine(widthArray)
	fmt.Fprint(w, rowBorder)
	for i := 0; i < rowCount; i++ {
		fmt.Fprint(w, "\n")
		writeTableRow(w, table, i, widthArray, styles...)
		fmt.Fprint(w, "\n")
		fmt.Fprint(w, rowBorder)
	}
	fmt.Fprint(w, "\n")
}

func rowBorderLine(widthArray []int) string {
	buf := bytes.NewBufferString(borderCross)
	for _, width := range widthArray {
		repeatWriteString(buf, borderRow, width+2)
		buf.WriteString(borderCross)
	}
	return buf.String()
}

func writeTableRow(w io.Writer, table Table, rowIndex int, widthArray []int, styles ...CellStyleFunc) {
	fmt.Fprint(w, borderCol)
	colCount := table.ColCount()
	for j := 0; j < colCount; j++ {
		fmt.Fprint(w, " ")
		format := fmt.Sprintf("%%-%ds", widthArray[j]+1)
		s := fmt.Sprintf(format, table.Get(rowIndex, j))
		for _, fn := range styles {
			s = fn(rowIndex, j, s)
		}
		fmt.Fprintf(w, s)
		fmt.Fprint(w, borderCol)
	}
}

func repeatWriteString(w io.Writer, s string, count int) {
	for i := 0; i < count; i++ {
		fmt.Fprint(w, s)
	}
}

// TableView represents a view of table, it implements Table interface, too
type TableView struct {
	table              Table
	rowIndex, colIndex int
	rowCount, colCount int
}

func (tv TableView) RowCount() int {
	return tv.rowCount
}

func (tv TableView) ColCount() int {
	return tv.colCount
}

func (tv TableView) Get(i, j int) string {
	return tv.table.Get(tv.rowCount+i, tv.colCount+j)
}

// ClipTable creates a view of table
func ClipTable(table Table, i, j, m, n int) Table {
	minR, minC := i, j
	maxR, maxC := i+m, j+n
	if minR < 0 || minC < 0 || minR > maxR || minC > maxC || maxR >= table.RowCount() || maxC >= table.ColCount() {
		panic("out of bound")
	}
	return &TableView{table, i, j, m, n}
}

// TableWithHeader add header for table
type TableWithHeader struct {
	table  Table
	header []string
}

func (twh TableWithHeader) RowCount() int { return twh.table.RowCount() + 1 }
func (twh TableWithHeader) ColCount() int { return twh.table.ColCount() }
func (twh TableWithHeader) Get(i, j int) string {
	if i == 0 {
		return twh.header[j]
	}
	return twh.table.Get(i-1, j)
}

func AddTableHeader(table Table, header []string) Table {
	return &TableWithHeader{table, header}
}

// 2-Array string
type StringMatrix [][]string

func (m StringMatrix) RowCount() int { return len(m) }
func (m StringMatrix) ColCount() int {
	if len(m) == 0 {
		return 0
	}
	return len(m[0])
}
func (m StringMatrix) Get(i, j int) string { return m[i][j] }
<commit_msg>fix table render<commit_after>package textutil

import (
	"bytes"
	"fmt"
	"io"

	"github.com/labstack/gommon/color"
)

const (
	borderCross = "+"
	borderRow   = "-"
	borderCol   = "|"
)

// Table represents a string-matrix
type Table interface {
	RowCount() int
	ColCount() int
	Get(i, j int) string
}

// TableStyler represents s render style for Table
type TableStyler interface {
	CellRender(row, col int, cell string, w *ColorWriter)
	BorderRender(border string, w *ColorWriter)
}

// ColorWriter
type ColorWriter struct {
	w io.Writer
	*color.Color
}

func (cw *ColorWriter) Write(b []byte) (int, error) {
	return cw.w.Write(b)
}

type colorable interface {
	Color() *color.Color
}

func newColorWriter(w io.Writer) *ColorWriter {
	if cw, ok := w.(colorable); ok {
		ret := &ColorWriter{w: w}
		ret.Color = cw.Color()
		return ret
	}
	ret := &ColorWriter{w: w}
	ret.Color = color.New()
	ret.Disable()
	return ret
}

// DefaultStyle ...
type DefaultStyle struct{}

func (style DefaultStyle) CellRender(row, col int, cell string, w *ColorWriter) {
	fmt.Fprint(w, cell)
}

func (style DefaultStyle) BorderRender(border string, w *ColorWriter) {
	fmt.Fprint(w, w.Grey(border))
}

var defaultStyle = DefaultStyle{}

// WriteTable formats table to writer with specified style
func WriteTable(w io.Writer, table Table, style TableStyler) {
	if style == nil {
		style = defaultStyle
	}
	rowCount, colCount := table.RowCount(), table.ColCount()
	if rowCount <= 0 || colCount <= 0 {
		return
	}
	widthArray := make([]int, colCount)
	for j := 0; j < colCount; j++ {
		maxWidth := 0
		for i := 0; i < rowCount; i++ {
			width := len(table.Get(i, j))
			if i == 0 || width > maxWidth {
				maxWidth = width
			}
		}
		widthArray[j] = maxWidth
	}
	cw := newColorWriter(w)
	rowBorder := rowBorderLine(widthArray)
	style.BorderRender(rowBorder, cw)
	for i := 0; i < rowCount; i++ {
		fmt.Fprint(cw, "\n")
		writeTableRow(cw, table, i, widthArray, style)
		fmt.Fprint(cw, "\n")
		style.BorderRender(rowBorder, cw)
	}
	fmt.Fprint(cw, "\n")
}

func rowBorderLine(widthArray []int) string {
	buf := bytes.NewBufferString(borderCross)
	for _, width := range widthArray {
		repeatWriteString(buf, borderRow, width+2)
		buf.WriteString(borderCross)
	}
	return buf.String()
}

func writeTableRow(cw *ColorWriter, table Table, rowIndex int, widthArray []int, style TableStyler) {
	style.BorderRender(borderCol, cw)
	colCount := table.ColCount()
	for j := 0; j < colCount; j++ {
		fmt.Fprint(cw, " ")
		format := fmt.Sprintf("%%-%ds", widthArray[j]+1)
		s := fmt.Sprintf(format, table.Get(rowIndex, j))
		style.CellRender(rowIndex, j, s, cw)
		style.BorderRender(borderCol, cw)
	}
}

func repeatWriteString(w io.Writer, s string, count int) {
	for i := 0; i < count; i++ {
		fmt.Fprint(w, s)
	}
}

// TableView represents a view of table, it implements Table interface, too
type TableView struct {
	table              Table
	rowIndex, colIndex int
	rowCount, colCount int
}

func (tv TableView) RowCount() int {
	return tv.rowCount
}

func (tv TableView) ColCount() int {
	return tv.colCount
}

func (tv TableView) Get(i, j int) string {
	return tv.table.Get(tv.rowCount+i, tv.colCount+j)
}

// ClipTable creates a view of table
func ClipTable(table Table, i, j, m, n int) Table {
	minR, minC := i, j
	maxR, maxC := i+m, j+n
	if minR < 0 || minC < 0 || minR > maxR || minC > maxC || maxR >= table.RowCount() || maxC >= table.ColCount() {
		panic("out of bound")
	}
	return &TableView{table, i, j, m, n}
}

// TableWithHeader add header for table
type TableWithHeader struct {
	table  Table
	header []string
}

func (twh TableWithHeader) RowCount() int { return twh.table.RowCount() + 1 }
func (twh TableWithHeader) ColCount() int { return twh.table.ColCount() }
func (twh TableWithHeader) Get(i, j int) string {
	if i == 0 {
		return twh.header[j]
	}
	return twh.table.Get(i-1, j)
}

func AddTableHeader(table Table, header []string) Table {
	return &TableWithHeader{table, header}
}

// 2-Array string
type StringMatrix [][]string

func (m StringMatrix) RowCount() int { return len(m) }
func (m StringMatrix) ColCount() int {
	if len(m) == 0 {
		return 0
	}
	return len(m[0])
}
func (m StringMatrix) Get(i, j int) string { return m[i][j] }
<|endoftext|>


<commit_before><commit_msg>Export Instana header constants<commit_after><|endoftext|>


<commit_before><commit_msg>reader_test: Refactor<commit_after><|endoftext|>


<commit_before>package spi

import (
	"reflect"
	"rtos"
	"sync/atomic"
	"sync/fence"
	"unsafe"

	"stm32/hal/dma"
)

type DriverError byte

const ErrTimeout DriverError = 1

func (e DriverError) Error() string {
	switch e {
	case ErrTimeout:
		return "timeout"
	default:
		return ""
	}
}

type Driver struct {
	deadline int64
	p        *Periph
	rxDMA    *dma.Channel
	txDMA    *dma.Channel
	dmacnt   int
	done     rtos.EventFlag
	err      uint32
}

// MakeDriver returns initialized SPI driver that uses provided SPI peripheral
// and DMA channels.
func MakeDriver(p *Periph, txdma, rxdma *dma.Channel) Driver {
	return Driver{p: p, rxDMA: rxdma, txDMA: txdma}
}

// NewDriver provides convenient way to create heap allocated Driver struct.
func NewDriver(p *Periph, txdma, rxdma *dma.Channel) *Driver {
	d := new(Driver)
	*d = MakeDriver(p, txdma, rxdma)
	return d
}

func (d *Driver) Periph() *Periph {
	return d.p
}

func (d *Driver) TxDMA() *dma.Channel {
	return d.txDMA
}

func (d *Driver) RxDMA() *dma.Channel {
	return d.rxDMA
}

func (d *Driver) DMAISR(ch *dma.Channel) {
	ev, err := ch.Status()
	if err&^dma.ErrFIFO != 0 {
		goto done
	}
	if ev&dma.Complete != 0 {
		ch.Clear(dma.Complete, 0)
		if atomic.AddInt(&d.dmacnt, -1) == 0 {
			goto done
		}
	}
	return
done:
	ch.DisableIRQ(dma.EvAll, dma.ErrAll)
	d.done.Signal(1)
}

func (d *Driver) DMAISR(ch dma.Channel) {
	ev, err := ch.Status()
	if ev&dma.Complete != 0 || err&^dma.ErrFIFO != 0 {
		ch.DisableIRQ(dma.EvAll, dma.ErrAll)
		ch.Disable() // required by non-stream DMA (eg. F0,F1,F3,L1,L4)
		if atomic.AddInt32(&d.dmacnt, -1) == 0 {
			d.done.Wakeup()
		}
	}
}

func (d *Driver) ISR() {
	d.p.DisableIRQ(RxNotEmpty | Err)
	d.done.Signal(1)
}

func (d *Driver) SetDeadline(deadline int64) {
	d.deadline = deadline
}

// WriteReadByte writes and reads byte.
func (d *Driver) WriteReadByte(b byte) byte {
	if d.err != 0 {
		return 0
	}
	p := d.p
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreByte(b)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint32(ErrTimeout) << 16
		return 0
	}
	b = p.LoadByte()
	if _, e := p.Status(); e != 0 {
		d.err = uint32(e) << 8
		return 0
	}
	return b
}

// WriteReadWord16 writes and reads 16-bit word.
func (d *Driver) WriteReadWord16(w uint16) uint16 {
	if d.err != 0 {
		return 0
	}
	p := d.p
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreWord16(w)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint32(ErrTimeout) << 16
		return 0
	}
	w = p.LoadWord16()
	if _, e := p.Status(); e != 0 {
		d.err = uint32(e) << 8
		return 0
	}
	return w
}

func (d *Driver) setupDMA(ch *dma.Channel, mode dma.Mode, wordSize uintptr) {
	ch.Setup(mode)
	ch.SetWordSize(wordSize, wordSize)
	ch.SetAddrP(unsafe.Pointer(d.p.raw.DR.Addr()))
}

func startDMA(ch *dma.Channel, addr uintptr, n int) {
	ch.SetAddrM(unsafe.Pointer(addr))
	ch.SetLen(n)
	ch.Clear(dma.EvAll, dma.ErrAll)
	ch.EnableIRQ(dma.Complete, dma.ErrAll&^dma.ErrFIFO)
	fence.W() // This orders writes to normal and I/O memory.
	ch.Enable()
}

func (d *Driver) writeReadDMA(out, in uintptr, olen, ilen int, wsize uintptr) (n int) {
	txdmacfg := dma.MTP | dma.FT4
	if olen > 1 {
		txdmacfg |= dma.IncM
	}
	d.setupDMA(d.txDMA, txdmacfg, 1)
	d.setupDMA(d.rxDMA, dma.PTM|dma.IncM|dma.FT4, wsize)
	p := d.p
	p.SetDuplex(Full)
	p.EnableDMA(RxNotEmpty | TxEmpty)
	p.EnableIRQ(Err)
	for {
		m := ilen - n
		if m == 0 {
			break
		}
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 2
		d.done.Reset(0)
		startDMA(d.rxDMA, in, m)
		startDMA(d.txDMA, out, m)
		if olen > 1 {
			out += uintptr(m)
		}
		in += uintptr(m)
		n += m
		done := d.done.Wait(1, d.deadline)
		if !done {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.rxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(ErrTimeout) << 16
			n -= d.rxDMA.Len()
			break
		}
		if _, e := p.Status(); e != 0 {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.rxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(e) << 8
			n -= d.rxDMA.Len()
			break
		}
		_, rxe := d.rxDMA.Status()
		_, txe := d.txDMA.Status()
		if e := (rxe | txe) &^ dma.ErrFIFO; e != 0 {
			d.err = uint32(e)
			n -= d.rxDMA.Len()
			break
		}
	}
	p.DisableDMA(RxNotEmpty | TxEmpty)
	p.DisableIRQ(Err)
	return
}

func (d *Driver) writeDMA(out uintptr, n int, wsize uintptr, incm dma.Mode) {
	d.setupDMA(d.txDMA, dma.MTP|incm|dma.FT4, wsize)
	p := d.p
	p.SetDuplex(HalfOut) // Avoid ErrOverflow.
	p.EnableDMA(TxEmpty)
	p.EnableIRQ(Err)
	for n > 0 {
		m := n
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 1
		d.done.Reset(0)
		startDMA(d.txDMA, out, m)
		n -= m
		if incm != 0 {
			out += uintptr(m)
		}
		done := d.done.Wait(1, d.deadline)
		if !done {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(ErrTimeout) << 16
			break
		}
		if _, e := p.Status(); e != 0 {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(e) << 8
			break
		}
		_, txe := d.txDMA.Status()
		if e := txe &^ dma.ErrFIFO; e != 0 {
			d.err = uint32(e)
			break
		}
	}
	p.DisableDMA(TxEmpty)
	p.DisableIRQ(Err)
	// Now DMA finished but SPI can still send buffered data. Wait for end.
	for {
		if ev, _ := p.Status(); ev&Busy == 0 {
			break
		}
		rtos.SchedYield()
	}
}

// Err returns value of internal error variable and clears it if clear is true.
func (d *Driver) Err(clear bool) error {
	e := d.err
	if e == 0 {
		return nil
	}
	if clear {
		d.err = 0
	}
	if err := DriverError(e >> 16); err != 0 {
		return err
	}
	if err := Error(e >> 8); err != 0 {
		if err&ErrOverrun != 0 && clear {
			d.p.LoadByte()
			d.p.Status()
		}
		return err
	}
	return dma.Error(e)
}

func (d *Driver) writeRead(oaddr, iaddr uintptr, olen, ilen int, wsize uintptr) int {
	if olen > ilen {
		var n int
		if ilen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
			if d.err != 0 {
				return n
			}
			olen -= ilen
			oaddr += uintptr(ilen)
		}
		d.writeDMA(oaddr, olen, wsize, dma.IncM)
		return n
	}
	if ilen > olen {
		var n int
		ffff := uint16(0xffff)
		if olen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, olen, olen, wsize)
			if d.err != 0 {
				return n
			}
			ilen -= olen
			iaddr += uintptr(olen)
			oaddr += uintptr(olen - 1)
		} else {
			oaddr = uintptr(unsafe.Pointer(&ffff))
		}
		return n + d.writeReadDMA(oaddr, iaddr, 1, ilen, wsize)
	}
	return d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
}

func (d *Driver) WriteStringRead(out string, in []byte) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one byte transfers.
		b := byte(0xff)
		if olen != 0 {
			b = out[0]
		}
		b = d.WriteReadByte(b)
		if ilen != 0 {
			in[0] = b
			return 1
		}
		return 0
	}
	oaddr := (*reflect.StringHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 1)
}

func (d *Driver) WriteRead(out, in []byte) int {
	return d.WriteStringRead(*(*string)(unsafe.Pointer(&out)), in)
}

func (d *Driver) WriteReadMany(oi ...[]byte) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []byte
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead(out, in)
	}
	return n
}

func (d *Driver) RepeatByte(b byte, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&b)), n, 1, 0)
	case n == 1:
		// Avoid DMA for one byte transfers.
		d.WriteReadByte(b)
	}
}

func (d *Driver) WriteRead16(out, in []uint16) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one word transfers.
		w := uint16(0xffff)
		if olen != 0 {
			w = out[0]
		}
		w = d.WriteReadWord16(w)
		if ilen != 0 {
			in[0] = w
			return 1
		}
		return 0
	}
	oaddr := (*reflect.SliceHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 2)
}

func (d *Driver) WriteReadMany16(oi ...[]uint16) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []uint16
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead16(out, in)
	}
	return n
}

func (d *Driver) RepeatWord16(w uint16, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&w)), n, 2, 0)
	case n == 1:
		// Avoid DMA for one word transfers.
		d.WriteReadWord16(w)
	}
}
<commit_msg>srm32/hal/spi: remove old DMAISR<commit_after>package spi

import (
	"reflect"
	"rtos"
	"sync/atomic"
	"sync/fence"
	"unsafe"

	"stm32/hal/dma"
)

type DriverError byte

const ErrTimeout DriverError = 1

func (e DriverError) Error() string {
	switch e {
	case ErrTimeout:
		return "timeout"
	default:
		return ""
	}
}

type Driver struct {
	deadline int64
	p        *Periph
	rxDMA    *dma.Channel
	txDMA    *dma.Channel
	dmacnt   int
	done     rtos.EventFlag
	err      uint32
}

// MakeDriver returns initialized SPI driver that uses provided SPI peripheral
// and DMA channels.
func MakeDriver(p *Periph, txdma, rxdma *dma.Channel) Driver {
	return Driver{p: p, rxDMA: rxdma, txDMA: txdma}
}

// NewDriver provides convenient way to create heap allocated Driver struct.
func NewDriver(p *Periph, txdma, rxdma *dma.Channel) *Driver {
	d := new(Driver)
	*d = MakeDriver(p, txdma, rxdma)
	return d
}

func (d *Driver) Periph() *Periph {
	return d.p
}

func (d *Driver) TxDMA() *dma.Channel {
	return d.txDMA
}

func (d *Driver) RxDMA() *dma.Channel {
	return d.rxDMA
}

func (d *Driver) DMAISR(ch *dma.Channel) {
	ev, err := ch.Status()
	if err&^dma.ErrFIFO != 0 {
		goto done
	}
	if ev&dma.Complete != 0 {
		ch.Clear(dma.Complete, 0)
		if atomic.AddInt(&d.dmacnt, -1) == 0 {
			goto done
		}
	}
	return
done:
	ch.DisableIRQ(dma.EvAll, dma.ErrAll)
	d.done.Signal(1)
}

func (d *Driver) ISR() {
	d.p.DisableIRQ(RxNotEmpty | Err)
	d.done.Signal(1)
}

func (d *Driver) SetDeadline(deadline int64) {
	d.deadline = deadline
}

// WriteReadByte writes and reads byte.
func (d *Driver) WriteReadByte(b byte) byte {
	if d.err != 0 {
		return 0
	}
	p := d.p
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreByte(b)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint32(ErrTimeout) << 16
		return 0
	}
	b = p.LoadByte()
	if _, e := p.Status(); e != 0 {
		d.err = uint32(e) << 8
		return 0
	}
	return b
}

// WriteReadWord16 writes and reads 16-bit word.
func (d *Driver) WriteReadWord16(w uint16) uint16 {
	if d.err != 0 {
		return 0
	}
	p := d.p
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreWord16(w)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint32(ErrTimeout) << 16
		return 0
	}
	w = p.LoadWord16()
	if _, e := p.Status(); e != 0 {
		d.err = uint32(e) << 8
		return 0
	}
	return w
}

func (d *Driver) setupDMA(ch *dma.Channel, mode dma.Mode, wordSize uintptr) {
	ch.Setup(mode)
	ch.SetWordSize(wordSize, wordSize)
	ch.SetAddrP(unsafe.Pointer(d.p.raw.DR.Addr()))
}

func startDMA(ch *dma.Channel, addr uintptr, n int) {
	ch.SetAddrM(unsafe.Pointer(addr))
	ch.SetLen(n)
	ch.Clear(dma.EvAll, dma.ErrAll)
	ch.EnableIRQ(dma.Complete, dma.ErrAll&^dma.ErrFIFO)
	fence.W() // This orders writes to normal and I/O memory.
	ch.Enable()
}

func (d *Driver) writeReadDMA(out, in uintptr, olen, ilen int, wsize uintptr) (n int) {
	txdmacfg := dma.MTP | dma.FT4
	if olen > 1 {
		txdmacfg |= dma.IncM
	}
	d.setupDMA(d.txDMA, txdmacfg, 1)
	d.setupDMA(d.rxDMA, dma.PTM|dma.IncM|dma.FT4, wsize)
	p := d.p
	p.SetDuplex(Full)
	p.EnableDMA(RxNotEmpty | TxEmpty)
	p.EnableIRQ(Err)
	for {
		m := ilen - n
		if m == 0 {
			break
		}
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 2
		d.done.Reset(0)
		startDMA(d.rxDMA, in, m)
		startDMA(d.txDMA, out, m)
		if olen > 1 {
			out += uintptr(m)
		}
		in += uintptr(m)
		n += m
		done := d.done.Wait(1, d.deadline)
		if !done {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.rxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(ErrTimeout) << 16
			n -= d.rxDMA.Len()
			break
		}
		if _, e := p.Status(); e != 0 {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.rxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(e) << 8
			n -= d.rxDMA.Len()
			break
		}
		_, rxe := d.rxDMA.Status()
		_, txe := d.txDMA.Status()
		if e := (rxe | txe) &^ dma.ErrFIFO; e != 0 {
			d.err = uint32(e)
			n -= d.rxDMA.Len()
			break
		}
	}
	p.DisableDMA(RxNotEmpty | TxEmpty)
	p.DisableIRQ(Err)
	return
}

func (d *Driver) writeDMA(out uintptr, n int, wsize uintptr, incm dma.Mode) {
	d.setupDMA(d.txDMA, dma.MTP|incm|dma.FT4, wsize)
	p := d.p
	p.SetDuplex(HalfOut) // Avoid ErrOverflow.
	p.EnableDMA(TxEmpty)
	p.EnableIRQ(Err)
	for n > 0 {
		m := n
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 1
		d.done.Reset(0)
		startDMA(d.txDMA, out, m)
		n -= m
		if incm != 0 {
			out += uintptr(m)
		}
		done := d.done.Wait(1, d.deadline)
		if !done {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(ErrTimeout) << 16
			break
		}
		if _, e := p.Status(); e != 0 {
			d.txDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(e) << 8
			break
		}
		_, txe := d.txDMA.Status()
		if e := txe &^ dma.ErrFIFO; e != 0 {
			d.err = uint32(e)
			break
		}
	}
	p.DisableDMA(TxEmpty)
	p.DisableIRQ(Err)
	// Now DMA finished but SPI can still send buffered data. Wait for end.
	for {
		if ev, _ := p.Status(); ev&Busy == 0 {
			break
		}
		rtos.SchedYield()
	}
}

// Err returns value of internal error variable and clears it if clear is true.
func (d *Driver) Err(clear bool) error {
	e := d.err
	if e == 0 {
		return nil
	}
	if clear {
		d.err = 0
	}
	if err := DriverError(e >> 16); err != 0 {
		return err
	}
	if err := Error(e >> 8); err != 0 {
		if err&ErrOverrun != 0 && clear {
			d.p.LoadByte()
			d.p.Status()
		}
		return err
	}
	return dma.Error(e)
}

func (d *Driver) writeRead(oaddr, iaddr uintptr, olen, ilen int, wsize uintptr) int {
	if olen > ilen {
		var n int
		if ilen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
			if d.err != 0 {
				return n
			}
			olen -= ilen
			oaddr += uintptr(ilen)
		}
		d.writeDMA(oaddr, olen, wsize, dma.IncM)
		return n
	}
	if ilen > olen {
		var n int
		ffff := uint16(0xffff)
		if olen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, olen, olen, wsize)
			if d.err != 0 {
				return n
			}
			ilen -= olen
			iaddr += uintptr(olen)
			oaddr += uintptr(olen - 1)
		} else {
			oaddr = uintptr(unsafe.Pointer(&ffff))
		}
		return n + d.writeReadDMA(oaddr, iaddr, 1, ilen, wsize)
	}
	return d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
}

func (d *Driver) WriteStringRead(out string, in []byte) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one byte transfers.
		b := byte(0xff)
		if olen != 0 {
			b = out[0]
		}
		b = d.WriteReadByte(b)
		if ilen != 0 {
			in[0] = b
			return 1
		}
		return 0
	}
	oaddr := (*reflect.StringHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 1)
}

func (d *Driver) WriteRead(out, in []byte) int {
	return d.WriteStringRead(*(*string)(unsafe.Pointer(&out)), in)
}

func (d *Driver) WriteReadMany(oi ...[]byte) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []byte
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead(out, in)
	}
	return n
}

func (d *Driver) RepeatByte(b byte, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&b)), n, 1, 0)
	case n == 1:
		// Avoid DMA for one byte transfers.
		d.WriteReadByte(b)
	}
}

func (d *Driver) WriteRead16(out, in []uint16) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one word transfers.
		w := uint16(0xffff)
		if olen != 0 {
			w = out[0]
		}
		w = d.WriteReadWord16(w)
		if ilen != 0 {
			in[0] = w
			return 1
		}
		return 0
	}
	oaddr := (*reflect.SliceHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 2)
}

func (d *Driver) WriteReadMany16(oi ...[]uint16) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []uint16
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead16(out, in)
	}
	return n
}

func (d *Driver) RepeatWord16(w uint16, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&w)), n, 2, 0)
	case n == 1:
		// Avoid DMA for one word transfers.
		d.WriteReadWord16(w)
	}
}
<|endoftext|>