<commit_before>/*
 * Copyright (C) Max Romanov
 * Copyright (C) NGINX, Inc.
 */

package unit

/*
#include "nxt_go_lib.h"
*/
import "C"

import (
	"net/http"
	"net/url"
	"sync"
)

type request struct {
	req   http.Request
	resp  *response
	c_req C.nxt_go_request_t
	id    C.uint32_t
	msgs  []*cmsg
	ch    chan *cmsg
}

func (r *request) Read(p []byte) (n int, err error) {
	c := C.size_t(cap(p))
	b := C.malloc(c)
	res := C.nxt_go_request_read(r.c_req, b, c)

	if res == -2 /* NXT_AGAIN */ {
		m := <-r.ch

		res = C.nxt_go_request_read_from(r.c_req, b, c, m.buf.b,
			m.buf.s)
		r.push(m)
	}

	if res > 0 {
		copy(p, C.GoBytes(b, res))
	}

	C.free(b)
	return int(res), nil
}

func (r *request) Close() error {
	C.nxt_go_request_close(r.c_req)
	return nil
}

type request_registry struct {
	sync.RWMutex
	m  map[C.nxt_go_request_t]*request
	id map[C.uint32_t]*request
}

var request_registry_ request_registry

func find_request(c_req C.nxt_go_request_t) *request {
	request_registry_.RLock()
	res := request_registry_.m[c_req]
	request_registry_.RUnlock()

	return res
}

func find_request_by_id(id C.uint32_t) *request {
	request_registry_.RLock()
	res := request_registry_.id[id]
	request_registry_.RUnlock()

	return res
}

func add_request(r *request) {
	request_registry_.Lock()
	if request_registry_.m == nil {
		request_registry_.m = make(map[C.nxt_go_request_t]*request)
		request_registry_.id = make(map[C.uint32_t]*request)
	}

	request_registry_.m[r.c_req] = r
	request_registry_.id[r.id] = r

	request_registry_.Unlock()
}

func remove_request(r *request) {
	request_registry_.Lock()
	if request_registry_.m != nil {
		delete(request_registry_.m, r.c_req)
		delete(request_registry_.id, r.id)
	}

	request_registry_.Unlock()
}

func (r *request) response() *response {
	if r.resp == nil {
		r.resp = new_response(r.c_req, &r.req)
	}

	return r.resp
}

func (r *request) done() {
	C.nxt_go_request_done(r.c_req)

	remove_request(r)

	for _, m := range r.msgs {
		m.Close()
	}

	if r.ch != nil {
		close(r.ch)
	}
}

func (r *request) push(m *cmsg) {
	r.msgs = append(r.msgs, m)
}

//export nxt_go_new_request
func nxt_go_new_request(c_req C.nxt_go_request_t, id C.uint32_t,
	c_method *C.nxt_go_str_t, c_uri *C.nxt_go_str_t) {

	uri := C.GoStringN(c_uri.start, c_uri.length)

	var URL *url.URL
	var err error
	if URL, err = url.ParseRequestURI(uri); err != nil {
		return
	}

	r := &request{
		req: http.Request{
			Method:     C.GoStringN(c_method.start, c_method.length),
			URL:        URL,
			Header:     http.Header{},
			Body:       nil,
			RequestURI: uri,
		},
		c_req: c_req,
		id:    id,
		msgs:  make([]*cmsg, 0, 1),
	}
	r.req.Body = r

	add_request(r)
}

//export nxt_go_find_request
func nxt_go_find_request(id C.uint32_t) C.nxt_go_request_t {
	r := find_request_by_id(id)

	if r != nil {
		return r.c_req
	}

	return 0
}

//export nxt_go_request_set_proto
func nxt_go_request_set_proto(c_req C.nxt_go_request_t, proto *C.nxt_go_str_t,
	maj C.int, min C.int) {

	r := find_request(c_req)
	r.req.Proto = C.GoStringN(proto.start, proto.length)
	r.req.ProtoMajor = int(maj)
	r.req.ProtoMinor = int(min)
}

//export nxt_go_request_add_header
func nxt_go_request_add_header(c_req C.nxt_go_request_t, name *C.nxt_go_str_t,
	value *C.nxt_go_str_t) {

	r := find_request(c_req)
	r.req.Header.Add(C.GoStringN(name.start, name.length),
		C.GoStringN(value.start, value.length))
}

//export nxt_go_request_set_content_length
func nxt_go_request_set_content_length(c_req C.nxt_go_request_t, l C.int64_t) {
	find_request(c_req).req.ContentLength = int64(l)
}

//export nxt_go_request_create_channel
func nxt_go_request_create_channel(c_req C.nxt_go_request_t) {
	find_request(c_req).ch = make(chan *cmsg)
}

//export nxt_go_request_set_host
func nxt_go_request_set_host(c_req C.nxt_go_request_t, host *C.nxt_go_str_t) {
	find_request(c_req).req.Host = C.GoStringN(host.start, host.length)
}

//export nxt_go_request_set_url
func nxt_go_request_set_url(c_req C.nxt_go_request_t, scheme *C.char) {
	find_request(c_req).req.URL.Scheme = C.GoString(scheme)
}

//export nxt_go_request_set_remote_addr
func nxt_go_request_set_remote_addr(c_req C.nxt_go_request_t,
	addr *C.nxt_go_str_t) {

	find_request(c_req).req.RemoteAddr = C.GoStringN(addr.start, addr.length)
}

//export nxt_go_request_serve
func nxt_go_request_serve(c_req C.nxt_go_request_t) {
	r := find_request(c_req)

	go func(r *request) {
		http.DefaultServeMux.ServeHTTP(r.response(), &r.req)
		r.done()
	}(r)
}
<commit_msg>Fixing Go program crash.<commit_after>/*
 * Copyright (C) Max Romanov
 * Copyright (C) NGINX, Inc.
 */

package unit

/*
#include "nxt_go_lib.h"
*/
import "C"

import (
	"net/http"
	"net/url"
	"sync"
)

type request struct {
	req   http.Request
	resp  *response
	c_req C.nxt_go_request_t
	id    C.uint32_t
	msgs  []*cmsg
	ch    chan *cmsg
}

func (r *request) Read(p []byte) (n int, err error) {
	c := C.size_t(cap(p))
	b := C.malloc(c)
	res := C.nxt_go_request_read(r.c_req, b, c)

	if res == -2 /* NXT_AGAIN */ {
		m := <-r.ch

		res = C.nxt_go_request_read_from(r.c_req, b, c, m.buf.b,
			m.buf.s)
		r.push(m)
	}

	if res > 0 {
		copy(p, C.GoBytes(b, res))
	}

	C.free(b)
	return int(res), nil
}

func (r *request) Close() error {
	C.nxt_go_request_close(r.c_req)
	return nil
}

type request_registry struct {
	sync.RWMutex
	m  map[C.nxt_go_request_t]*request
	id map[C.uint32_t]*request
}

var request_registry_ request_registry

func find_request(c_req C.nxt_go_request_t) *request {
	request_registry_.RLock()
	res := request_registry_.m[c_req]
	request_registry_.RUnlock()

	return res
}

func find_request_by_id(id C.uint32_t) *request {
	request_registry_.RLock()
	res := request_registry_.id[id]
	request_registry_.RUnlock()

	return res
}

func add_request(r *request) {
	request_registry_.Lock()
	if request_registry_.m == nil {
		request_registry_.m = make(map[C.nxt_go_request_t]*request)
		request_registry_.id = make(map[C.uint32_t]*request)
	}

	request_registry_.m[r.c_req] = r
	request_registry_.id[r.id] = r

	request_registry_.Unlock()
}

func remove_request(r *request) {
	request_registry_.Lock()
	if request_registry_.m != nil {
		delete(request_registry_.m, r.c_req)
		delete(request_registry_.id, r.id)
	}

	request_registry_.Unlock()
}

func (r *request) response() *response {
	if r.resp == nil {
		r.resp = new_response(r.c_req, &r.req)
	}

	return r.resp
}

func (r *request) done() {
	remove_request(r)

	C.nxt_go_request_done(r.c_req)

	for _, m := range r.msgs {
		m.Close()
	}

	if r.ch != nil {
		close(r.ch)
	}
}

func (r *request) push(m *cmsg) {
	r.msgs = append(r.msgs, m)
}

//export nxt_go_new_request
func nxt_go_new_request(c_req C.nxt_go_request_t, id C.uint32_t,
	c_method *C.nxt_go_str_t, c_uri *C.nxt_go_str_t) {

	uri := C.GoStringN(c_uri.start, c_uri.length)

	var URL *url.URL
	var err error
	if URL, err = url.ParseRequestURI(uri); err != nil {
		return
	}

	r := &request{
		req: http.Request{
			Method:     C.GoStringN(c_method.start, c_method.length),
			URL:        URL,
			Header:     http.Header{},
			Body:       nil,
			RequestURI: uri,
		},
		c_req: c_req,
		id:    id,
		msgs:  make([]*cmsg, 0, 1),
	}
	r.req.Body = r

	add_request(r)
}

//export nxt_go_find_request
func nxt_go_find_request(id C.uint32_t) C.nxt_go_request_t {
	r := find_request_by_id(id)

	if r != nil {
		return r.c_req
	}

	return 0
}

//export nxt_go_request_set_proto
func nxt_go_request_set_proto(c_req C.nxt_go_request_t, proto *C.nxt_go_str_t,
	maj C.int, min C.int) {

	r := find_request(c_req)
	r.req.Proto = C.GoStringN(proto.start, proto.length)
	r.req.ProtoMajor = int(maj)
	r.req.ProtoMinor = int(min)
}

//export nxt_go_request_add_header
func nxt_go_request_add_header(c_req C.nxt_go_request_t, name *C.nxt_go_str_t,
	value *C.nxt_go_str_t) {

	r := find_request(c_req)
	r.req.Header.Add(C.GoStringN(name.start, name.length),
		C.GoStringN(value.start, value.length))
}

//export nxt_go_request_set_content_length
func nxt_go_request_set_content_length(c_req C.nxt_go_request_t, l C.int64_t) {
	find_request(c_req).req.ContentLength = int64(l)
}

//export nxt_go_request_create_channel
func nxt_go_request_create_channel(c_req C.nxt_go_request_t) {
	find_request(c_req).ch = make(chan *cmsg)
}

//export nxt_go_request_set_host
func nxt_go_request_set_host(c_req C.nxt_go_request_t, host *C.nxt_go_str_t) {
	find_request(c_req).req.Host = C.GoStringN(host.start, host.length)
}

//export nxt_go_request_set_url
func nxt_go_request_set_url(c_req C.nxt_go_request_t, scheme *C.char) {
	find_request(c_req).req.URL.Scheme = C.GoString(scheme)
}

//export nxt_go_request_set_remote_addr
func nxt_go_request_set_remote_addr(c_req C.nxt_go_request_t,
	addr *C.nxt_go_str_t) {

	find_request(c_req).req.RemoteAddr = C.GoStringN(addr.start, addr.length)
}

//export nxt_go_request_serve
func nxt_go_request_serve(c_req C.nxt_go_request_t) {
	r := find_request(c_req)

	go func(r *request) {
		http.DefaultServeMux.ServeHTTP(r.response(), &r.req)
		r.done()
	}(r)
}
<|endoftext|>


<commit_before>package s3gof3r

import (
	"bytes"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"testing"
	"time"
)

type getTest struct {
	path   string
	data   io.Reader
	config *Config
	rSize  int64
	err    error
}

var getTests = []getTest{
	{"t1.test", &randSrc{Size: int(1 * kb)}, nil, 1024, nil},
	{"NoKey", nil, nil, 0, &respError{StatusCode: 404, Message: "The specified key does not exist."}},
	{"30mb_test", &randSrc{Size: int(30 * mb)}, nil, 30 * mb, nil},
}

func TestGetReader(t *testing.T) {
	SetLogger(os.Stderr, "test: ", (log.LstdFlags | log.Lshortfile), true)
	b, err := testBucket()
	if err != nil {
		t.Fatal(err)
	}
	for _, tt := range getTests {
		if tt.data != nil {
			err = b.putReader(tt.path, &tt.data)
			if err != nil {
				t.Fatal(err)
			}
		}

		r, h, err := b.GetReader(tt.path, tt.config)
		if err != nil {
			errComp(tt.err, err, t, tt)
			continue
		}
		t.Logf("headers %v\n", h)
		w := ioutil.Discard

		n, err := io.Copy(w, r)
		if err != nil {
			t.Error(err)
		}
		if n != tt.rSize {
			t.Errorf("Expected size: %d. Actual: %d", tt.rSize, n)

		}
		err = r.Close()
		errComp(tt.err, err, t, tt)

	}
}

type putTest struct {
	path   string
	data   []byte
	header http.Header
	config *Config
	wSize  int64
	err    error
}

var putTests = []putTest{
	{"testfile", []byte("test_data"), nil, nil, 9, nil},
	{"", []byte("test_data"), nil, nil,
		9, &respError{StatusCode: 400, Message: "A key must be specified"}},
	{"testempty", []byte(""), nil, nil, 0, errors.New("0 bytes written")},
	{"testhg", []byte("foo"), goodHeader(), nil, 3, nil},
	{"testhb", []byte("foo"), badHeader(), nil, 3,
		&respError{StatusCode: 400, Message: "The Encryption request you specified is not valid. Supported value: AES256."}},
	{"nomd5", []byte("foo"), goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 1, Md5Check: false, Scheme: "http", Client: http.DefaultClient}, 3, nil},
	{"noconc", []byte("foo"), nil,
		&Config{Concurrency: 0, PartSize: 5 * mb, NTry: 1, Md5Check: true, Scheme: "https", Client: ClientWithTimeout(5 * time.Second)}, 3, nil},
}

func TestPutWriter(t *testing.T) {
	b, err := testBucket()
	if err != nil {
		t.Fatal(err)
	}
	for _, tt := range putTests {
		w, err := b.PutWriter(tt.path, tt.header, tt.config)
		if err != nil {
			errComp(tt.err, err, t, tt)
			continue
		}
		r := bytes.NewReader(tt.data)

		n, err := io.Copy(w, r)
		if err != nil {
			t.Error(err)
		}
		if n != tt.wSize {
			t.Errorf("Expected size: %d. Actual: %d", tt.wSize, n)

		}
		err = w.Close()
		errComp(tt.err, err, t, tt)
	}
}

type putMultiTest struct {
	path   string
	data   io.Reader
	header http.Header
	config *Config
	wSize  int64
	err    error
}

var putMultiTests = []putMultiTest{
	{"5mb_test.test", &randSrc{Size: int(5 * mb)}, goodHeader(), nil, 5 * mb, nil},
	{"20mb_test.test", &randSrc{Size: int(20 * mb)}, goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 2, Md5Check: true, Scheme: "https",
			Client: ClientWithTimeout(5 * time.Second)}, 20 * mb, nil},
	{"timeout.test", &randSrc{Size: int(5 * mb)}, goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 1, Md5Check: false, Scheme: "https",
			Client: ClientWithTimeout(1 * time.Millisecond)}, 5 * mb,
		errors.New("timeout")},
	{"timeout.test", &randSrc{Size: int(10 * mb)}, goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 1, Md5Check: true, Scheme: "https",
			Client: ClientWithTimeout(100 * time.Millisecond)}, 10 * mb,
		errors.New("timeout")},
	{"smallpart", &randSrc{Size: int(10 * mb)}, goodHeader(),
		&Config{Concurrency: 4, PartSize: 4 * mb, NTry: 3, Md5Check: false, Scheme: "https",
			Client: ClientWithTimeout(5 * time.Second)}, 10 * mb, nil},
}

func TestPutMulti(t *testing.T) {
	t.Parallel()
	b, err := testBucket()
	if err != nil {
		t.Fatal(err)
	}
	SetLogger(os.Stderr, "test: ", (log.LstdFlags | log.Lshortfile), true)
	for _, tt := range putMultiTests {
		w, err := b.PutWriter(tt.path, tt.header, tt.config)
		if err != nil {
			errComp(tt.err, err, t, tt)
			continue
		}
		n, err := io.Copy(w, tt.data)
		if err != nil {
			t.Error(err)
		}
		if n != tt.wSize {
			t.Errorf("Expected size: %d. Actual: %d", tt.wSize, n)

		}
		err = w.Close()
		errComp(tt.err, err, t, tt)
	}
}

type tB struct {
	*Bucket
}

func testBucket() (*tB, error) {
	k, err := InstanceKeys()
	if err != nil {
		k, err = EnvKeys()
		if err != nil {
			return nil, err
		}
	}
	bucket := os.Getenv("TEST_BUCKET")
	if bucket == "" {
		return nil, errors.New("TEST_BUCKET must be set in environment")

	}
	s3 := New("", k)
	b := tB{s3.Bucket(bucket)}

	return &b, err
}

func (b *tB) putReader(path string, r *io.Reader) error {

	if r == nil {
		return nil // special handling for nil case
	}

	w, err := b.PutWriter(path, nil, nil)
	if err != nil {
		return err
	}
	_, err = io.Copy(w, *r)
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	return nil
}

func errComp(expect, actual error, t *testing.T, tt interface{}) bool {

	if expect == nil && actual == nil {
		return true
	}

	if expect == nil || actual == nil {
		t.Errorf("PutWriter called with %v\n Expected: %v\n Actual:   %v\n", tt, expect, actual)
		return false
	}
	if !strings.Contains(actual.Error(), expect.Error()) {
		t.Errorf("PutWriter called with %v\n Expected: %v\n Actual:   %v\n", tt, expect, actual)
		return false
	}
	return true

}

func goodHeader() http.Header {
	header := make(http.Header)
	header.Add("x-amz-server-side-encryption", "AES256")
	header.Add("x-amz-meta-foometadata", "testmeta")
	return header
}

func badHeader() http.Header {
	header := make(http.Header)
	header.Add("x-amz-server-side-encryption", "AES512")
	return header
}

type randSrc struct {
	Size  int
	total int
}

func (r *randSrc) Read(p []byte) (int, error) {

	n, err := rand.Read(p)
	r.total = r.total + n
	if r.total >= r.Size {
		return n - (r.total - r.Size), io.EOF
	}
	return n, err
}

func ExampleBucket_PutWriter() error {

	k, err := EnvKeys() // get S3 keys from environment
	if err != nil {
		return err
	}
	// Open bucket to put file into
	s3 := New("", k)
	b := s3.Bucket("bucketName")

	// open file to upload
	file, err := os.Open("fileName")
	if err != nil {
		return err
	}

	// Open a PutWriter for upload
	w, err := b.PutWriter(file.Name(), nil, nil)
	if err != nil {
		return err
	}
	if _, err = io.Copy(w, file); err != nil { // Copy into S3
		return err
	}
	if err = w.Close(); err != nil {
		return err
	}
	return nil
}

func ExampleBucket_GetReader() error {

	k, err := EnvKeys() // get S3 keys from environment
	if err != nil {
		return err
	}

	// Open bucket to put file into
	s3 := New("", k)
	b := s3.Bucket("bucketName")

	r, h, err := b.GetReader("keyName", nil)
	if err != nil {
		return err
	}
	// stream to standard output
	if _, err = io.Copy(os.Stdout, r); err != nil {
		return err
	}
	err = r.Close()
	if err != nil {
		return err
	}
	fmt.Println(h) // print key header data
	return nil
}
<commit_msg>url encoding putter test.<commit_after>package s3gof3r

import (
	"bytes"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"testing"
	"time"
)

type getTest struct {
	path   string
	data   io.Reader
	config *Config
	rSize  int64
	err    error
}

var getTests = []getTest{
	{"t1.test", &randSrc{Size: int(1 * kb)}, nil, 1024, nil},
	{"NoKey", nil, nil, 0, &respError{StatusCode: 404, Message: "The specified key does not exist."}},
	{"10_mb_test",
		&randSrc{Size: int(10 * mb)},
		&Config{Concurrency: 3, PartSize: 3 * mb, NTry: 2, Md5Check: true, Scheme: "https", Client: ClientWithTimeout(5 * time.Second)},
		10 * mb,
		nil},
}

func TestGetReader(t *testing.T) {
	SetLogger(os.Stderr, "test: ", (log.LstdFlags | log.Lshortfile), true)
	b, err := testBucket()
	if err != nil {
		t.Fatal(err)
	}
	for _, tt := range getTests {
		if tt.data != nil {
			err = b.putReader(tt.path, &tt.data)
			if err != nil {
				t.Fatal(err)
			}
		}

		r, h, err := b.GetReader(tt.path, tt.config)
		if err != nil {
			errComp(tt.err, err, t, tt)
			continue
		}
		t.Logf("headers %v\n", h)
		w := ioutil.Discard

		n, err := io.Copy(w, r)
		if err != nil {
			t.Error(err)
		}
		if n != tt.rSize {
			t.Errorf("Expected size: %d. Actual: %d", tt.rSize, n)

		}
		err = r.Close()
		errComp(tt.err, err, t, tt)

	}
}

type putTest struct {
	path   string
	data   []byte
	header http.Header
	config *Config
	wSize  int64
	err    error
}

var putTests = []putTest{
	{"testfile", []byte("test_data"), nil, nil, 9, nil},
	{"", []byte("test_data"), nil, nil,
		9, &respError{StatusCode: 400, Message: "A key must be specified"}},
	{"testempty", []byte(""), nil, nil, 0, errors.New("0 bytes written")},
	{"testhg", []byte("foo"), goodHeader(), nil, 3, nil},
	{"testhb", []byte("foo"), badHeader(), nil, 3,
		&respError{StatusCode: 400, Message: "The Encryption request you specified is not valid. Supported value: AES256."}},
	{"nomd5", []byte("foo"), goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 1, Md5Check: false, Scheme: "http", Client: http.DefaultClient}, 3, nil},
	{"noconc", []byte("foo"), nil,
		&Config{Concurrency: 0, PartSize: 5 * mb, NTry: 1, Md5Check: true, Scheme: "https", Client: ClientWithTimeout(5 * time.Second)}, 3, nil},
	{"enc test", []byte("test_data"), nil, nil, 9, nil},
}

func TestPutWriter(t *testing.T) {
	b, err := testBucket()
	if err != nil {
		t.Fatal(err)
	}
	for _, tt := range putTests {
		w, err := b.PutWriter(tt.path, tt.header, tt.config)
		if err != nil {
			errComp(tt.err, err, t, tt)
			continue
		}
		r := bytes.NewReader(tt.data)

		n, err := io.Copy(w, r)
		if err != nil {
			t.Error(err)
		}
		if n != tt.wSize {
			t.Errorf("Expected size: %d. Actual: %d", tt.wSize, n)

		}
		err = w.Close()
		errComp(tt.err, err, t, tt)
	}
}

type putMultiTest struct {
	path   string
	data   io.Reader
	header http.Header
	config *Config
	wSize  int64
	err    error
}

var putMultiTests = []putMultiTest{
	{"5mb_test.test", &randSrc{Size: int(5 * mb)}, goodHeader(), nil, 5 * mb, nil},
	{"11mb_test.test", &randSrc{Size: int(11 * mb)}, goodHeader(),
		&Config{Concurrency: 3, PartSize: 5 * mb, NTry: 2, Md5Check: true, Scheme: "https",
			Client: ClientWithTimeout(5 * time.Second)}, 11 * mb, nil},
	{"timeout.test", &randSrc{Size: int(5 * mb)}, goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 1, Md5Check: false, Scheme: "https",
			Client: ClientWithTimeout(1 * time.Millisecond)}, 5 * mb,
		errors.New("timeout")},
	{"timeout.test", &randSrc{Size: int(10 * mb)}, goodHeader(),
		&Config{Concurrency: 1, PartSize: 5 * mb, NTry: 1, Md5Check: true, Scheme: "https",
			Client: ClientWithTimeout(100 * time.Millisecond)}, 10 * mb,
		errors.New("timeout")},
	{"smallpart", &randSrc{Size: int(6 * mb)}, goodHeader(),
		&Config{Concurrency: 4, PartSize: 2 * mb, NTry: 3, Md5Check: false, Scheme: "https",
			Client: ClientWithTimeout(5 * time.Second)}, 6 * mb, nil},
}

func TestPutMulti(t *testing.T) {
	t.Parallel()
	b, err := testBucket()
	if err != nil {
		t.Fatal(err)
	}
	SetLogger(os.Stderr, "test: ", (log.LstdFlags | log.Lshortfile), true)
	for _, tt := range putMultiTests {
		w, err := b.PutWriter(tt.path, tt.header, tt.config)
		if err != nil {
			errComp(tt.err, err, t, tt)
			continue
		}
		n, err := io.Copy(w, tt.data)
		if err != nil {
			t.Error(err)
		}
		if n != tt.wSize {
			t.Errorf("Expected size: %d. Actual: %d", tt.wSize, n)

		}
		err = w.Close()
		errComp(tt.err, err, t, tt)
	}
}

type tB struct {
	*Bucket
}

func testBucket() (*tB, error) {
	k, err := InstanceKeys()
	if err != nil {
		k, err = EnvKeys()
		if err != nil {
			return nil, err
		}
	}
	bucket := os.Getenv("TEST_BUCKET")
	if bucket == "" {
		return nil, errors.New("TEST_BUCKET must be set in environment")

	}
	s3 := New("", k)
	b := tB{s3.Bucket(bucket)}

	return &b, err
}

func (b *tB) putReader(path string, r *io.Reader) error {

	if r == nil {
		return nil // special handling for nil case
	}

	w, err := b.PutWriter(path, nil, nil)
	if err != nil {
		return err
	}
	_, err = io.Copy(w, *r)
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	return nil
}

func errComp(expect, actual error, t *testing.T, tt interface{}) bool {

	if expect == nil && actual == nil {
		return true
	}

	if expect == nil || actual == nil {
		t.Errorf("PutWriter called with %v\n Expected: %v\n Actual:   %v\n", tt, expect, actual)
		return false
	}
	if !strings.Contains(actual.Error(), expect.Error()) {
		t.Errorf("PutWriter called with %v\n Expected: %v\n Actual:   %v\n", tt, expect, actual)
		return false
	}
	return true

}

func goodHeader() http.Header {
	header := make(http.Header)
	header.Add("x-amz-server-side-encryption", "AES256")
	header.Add("x-amz-meta-foometadata", "testmeta")
	return header
}

func badHeader() http.Header {
	header := make(http.Header)
	header.Add("x-amz-server-side-encryption", "AES512")
	return header
}

type randSrc struct {
	Size  int
	total int
}

func (r *randSrc) Read(p []byte) (int, error) {

	n, err := rand.Read(p)
	r.total = r.total + n
	if r.total >= r.Size {
		return n - (r.total - r.Size), io.EOF
	}
	return n, err
}

func ExampleBucket_PutWriter() error {

	k, err := EnvKeys() // get S3 keys from environment
	if err != nil {
		return err
	}
	// Open bucket to put file into
	s3 := New("", k)
	b := s3.Bucket("bucketName")

	// open file to upload
	file, err := os.Open("fileName")
	if err != nil {
		return err
	}

	// Open a PutWriter for upload
	w, err := b.PutWriter(file.Name(), nil, nil)
	if err != nil {
		return err
	}
	if _, err = io.Copy(w, file); err != nil { // Copy into S3
		return err
	}
	if err = w.Close(); err != nil {
		return err
	}
	return nil
}

func ExampleBucket_GetReader() error {

	k, err := EnvKeys() // get S3 keys from environment
	if err != nil {
		return err
	}

	// Open bucket to put file into
	s3 := New("", k)
	b := s3.Bucket("bucketName")

	r, h, err := b.GetReader("keyName", nil)
	if err != nil {
		return err
	}
	// stream to standard output
	if _, err = io.Copy(os.Stdout, r); err != nil {
		return err
	}
	err = r.Close()
	if err != nil {
		return err
	}
	fmt.Println(h) // print key header data
	return nil
}
<|endoftext|>


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

package oslogin

import (
	"bufio"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func TestOsLoginEnabled(t *testing.T) {
	// Check OS Login enabled in /etc/nsswitch.conf
	file, err := os.Open("/etc/nsswitch.conf")
	if err != nil {
		t.Fatalf("cannot read /etc/nsswitch.conf")
	}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "passwd:") {
			if !strings.Contains(scanner.Text(), "oslogin") {
				t.Errorf("OS Login not enabled in /etc/nsswitch.conf.")
			}
		}

	}
	file.Close()

	// Check AuthorizedKeys Command
	file, err = os.Open("/etc/ssh/sshd_config")
	if err != nil {
		t.Fatalf("cannot read /etc/ssh/sshd_config")
	}
	scanner = bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.HasPrefix(scanner.Text(), "AuthorizedKeysCommand ") {
			if !strings.Contains(scanner.Text(), "/usr/bin/google_authorized_keys") {
				t.Errorf("AuthorizedKeysCommand not set up for OS Login.")
			}
		}

	}
	file.Close()

	// Check Pam Modules
	data, err := ioutil.ReadFile("/etc/pam.d/sshd")
	if err != nil {
		t.Fatalf("cannot read /etc/pam.d/sshd")
	}
	contents := string(data)
	if !strings.Contains(contents, "pam_oslogin_login.so") {
		t.Errorf("OS Login module pam_oslogin_login.so not set up.")
	}
	if !strings.Contains(contents, "pam_oslogin_admin.so") {
		t.Errorf("OS Login module pam_oslogin_admin.so not set up.")
	}
}

func TestOsLoginDisabled(t *testing.T) {
	// Check OS Login not enabled in /etc/nsswitch.conf
	file, err := os.Open("/etc/nsswitch.conf")
	if err != nil {
		t.Fatalf("cannot read /etc/nsswitch.conf")
	}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "passwd:") {
			if strings.Contains(scanner.Text(), "oslogin") {
				t.Errorf("OS Login enabled in /etc/nsswitch.conf.")
			}
		}

	}
	file.Close()

	// Check AuthorizedKeys Command
	file, err = os.Open("/etc/ssh/sshd_config")
	if err != nil {
		t.Fatalf("cannot read /etc/ssh/sshd_config")
	}
	scanner = bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.HasPrefix(scanner.Text(), "AuthorizedKeysCommand ") {
			if strings.Contains(scanner.Text(), "/usr/bin/google_authorized_keys") {
				t.Errorf("AuthorizedKeysCommand is set up for OS Login.")
			}
		}

	}
	file.Close()

	// Check Pam Modules
	data, err := ioutil.ReadFile("/etc/pam.d/sshd")
	if err != nil {
		t.Fatalf("cannot read /etc/pam.d/sshd")
	}
	contents := string(data)
	if strings.Contains(contents, "pam_oslogin_login.so") {
		t.Errorf("OS Login module pam_oslogin_login.so is set up.")
	}
	if strings.Contains(contents, "pam_oslogin_admin.so") {
		t.Errorf("OS Login module pam_oslogin_admin.so is set up.")
	}
}
<commit_msg>refactor OS Login test (#267)<commit_after>//go:build cit
// +build cit

package oslogin

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

func TestOsLoginEnabled(t *testing.T) {
	// Check OS Login enabled in /etc/nsswitch.conf
	data, err := ioutil.ReadFile("/etc/nsswitch.conf")
	if err != nil {
		t.Fatalf("cannot read /etc/nsswitch.conf")
	}
	for _, line := range strings.Split(string(data), "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}
		if strings.Contains(line, "passwd:") && !strings.Contains(line, "oslogin") {
			t.Errorf("OS Login not enabled in /etc/nsswitch.conf.")
		}
	}

	// Check AuthorizedKeys Command
	data, err = ioutil.ReadFile("/etc/ssh/sshd_config")
	if err != nil {
		t.Fatalf("cannot read /etc/ssh/sshd_config")
	}
	for _, line := range strings.Split(string(data), "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}
		if strings.Contains(line, "AuthorizedKeysCommand") && !strings.Contains(line, "/usr/bin/google_authorized_keys") {
			t.Errorf("AuthorizedKeysCommand not set up for OS Login.")
		}
	}

	// Check Pam Modules
	data, err = ioutil.ReadFile("/etc/pam.d/sshd")
	if err != nil {
		t.Fatalf("cannot read /etc/pam.d/sshd")
	}
	contents := string(data)
	if !strings.Contains(contents, "pam_oslogin_login.so") || !strings.Contains(contents, "pam_oslogin_admin.so") {
		t.Errorf("OS Login PAM module missing from pam.d/sshd.")
	}
}

func TestOsLoginDisabled(t *testing.T) {
	// Check OS Login not enabled in /etc/nsswitch.conf
	data, err := ioutil.ReadFile("/etc/nsswitch.conf")
	if err != nil {
		t.Fatalf("cannot read /etc/nsswitch.conf")
	}
	for _, line := range strings.Split(string(data), "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}
		if strings.Contains(line, "passwd:") && !strings.Contains(line, "oslogin") {
			t.Errorf("OS Login NSS module wrongly included in /etc/nsswitch.conf when disabled.")
		}
	}

	// Check AuthorizedKeys Command
	data, err = ioutil.ReadFile("/etc/ssh/sshd_config")
	if err != nil {
		t.Fatalf("cannot read /etc/ssh/sshd_config")
	}
	for _, line := range strings.Split(string(data), "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}
		if strings.Contains(line, "AuthorizedKeysCommand") && strings.Contains(line, "/usr/bin/google_authorized_keys") {
			t.Errorf("OS Login AuthorizedKeysCommand directive wrongly exists when disabled.")
		}
	}

	// Check Pam Modules
	data, err = ioutil.ReadFile("/etc/pam.d/sshd")
	if err != nil {
		t.Fatalf("cannot read /etc/pam.d/sshd")
	}
	contents := string(data)
	if strings.Contains(contents, "pam_oslogin_login.so") || strings.Contains(contents, "pam_oslogin_admin.so") {
		t.Errorf("OS Login PAM module wrongly included in pam.d/sshd when disabled.")
	}
}
<|endoftext|>


<commit_before>package demoinfocs

import (
	"errors"
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/markus-wa/go-unassert"
	dispatch "github.com/markus-wa/godispatch"

	"github.com/markus-wa/demoinfocs-golang/common"
	"github.com/markus-wa/demoinfocs-golang/events"
	"github.com/markus-wa/demoinfocs-golang/msg"
)

const maxOsPath = 260

const (
	playerWeaponPrefix    = "m_hMyWeapons."
	playerWeaponPrePrefix = "bcc_nonlocaldata."
	gameRulesPrefix       = "cs_gamerules_data"
)

// Parsing errors
var (
	// ErrCancelled signals that parsing was cancelled via Parser.Cancel()
	ErrCancelled = errors.New("parsing was cancelled before it finished (ErrCancelled)")

	// ErrUnexpectedEndOfDemo signals that the demo is incomplete / corrupt -
	// these demos may still be useful, check how far the parser got.
	ErrUnexpectedEndOfDemo = errors.New("demo stream ended unexpectedly (ErrUnexpectedEndOfDemo)")

	// ErrInvalidFileType signals that the input isn't a valid CS:GO demo.
	ErrInvalidFileType = errors.New("invalid File-Type; expecting HL2DEMO in the first 8 bytes (ErrInvalidFileType)")
)

// ParseHeader attempts to parse the header of the demo and returns it.
// If not done manually this will be called by Parser.ParseNextFrame() or Parser.ParseToEnd().
//
// Returns ErrInvalidFileType if the filestamp (first 8 bytes) doesn't match HL2DEMO.
func (p *Parser) ParseHeader() (common.DemoHeader, error) {
	var h common.DemoHeader
	h.Filestamp = p.bitReader.ReadCString(8)
	h.Protocol = p.bitReader.ReadSignedInt(32)
	h.NetworkProtocol = p.bitReader.ReadSignedInt(32)
	h.ServerName = p.bitReader.ReadCString(maxOsPath)
	h.ClientName = p.bitReader.ReadCString(maxOsPath)
	h.MapName = p.bitReader.ReadCString(maxOsPath)
	h.GameDirectory = p.bitReader.ReadCString(maxOsPath)
	h.PlaybackTime = time.Duration(p.bitReader.ReadFloat() * float32(time.Second))
	h.PlaybackTicks = p.bitReader.ReadSignedInt(32)
	h.PlaybackFrames = p.bitReader.ReadSignedInt(32)
	h.SignonLength = p.bitReader.ReadSignedInt(32)

	if h.Filestamp != "HL2DEMO" {
		return h, ErrInvalidFileType
	}

	// Initialize queue if the buffer size wasn't specified, the amount of ticks
	// seems to be a good indicator of how many events we'll get
	if p.msgQueue == nil {
		p.initMsgQueue(h.PlaybackTicks)
	}

	p.header = &h

	return h, nil
}

// ParseToEnd attempts to parse the demo until the end.
// Aborts and returns ErrCancelled if Cancel() is called before the end.
//
// See also: ParseNextFrame() for other possible errors.
func (p *Parser) ParseToEnd() (err error) {
	defer func() {
		// Make sure all the messages of the demo are handled
		p.msgDispatcher.SyncAllQueues()

		// Close msgQueue
		if p.msgQueue != nil {
			close(p.msgQueue)
		}

		if err == nil {
			err = recoverFromPanic(recover())
		}
	}()

	if p.header == nil {
		_, err = p.ParseHeader()
		if err != nil {
			return
		}
	}

	for {
		select {
		case <-p.cancelChan:
			return ErrCancelled

		default:
			if !p.parseFrame() {
				return p.error()
			}
		}

		if err = p.error(); err != nil {
			return
		}
	}
}

func recoverFromPanic(r interface{}) error {
	if r == nil {
		return nil
	}

	if r == io.ErrUnexpectedEOF || r == io.EOF {
		return ErrUnexpectedEndOfDemo
	}

	switch err := r.(type) {
	case dispatch.ConsumerCodePanic:
		panic(err.Value())
	case error:
		return err
	case string:
		return errors.New(err)
	default:
		return fmt.Errorf("unexpected error: %v", err)
	}
}

// Cancel aborts ParseToEnd().
// All information that was already read up to this point may still be used (and new events may still be sent out).
func (p *Parser) Cancel() {
	p.cancelChan <- struct{}{}
}

/*
ParseNextFrame attempts to parse the next frame / demo-tick (not ingame tick).

Returns true unless the demo command 'stop' or an error was encountered.

May return ErrUnexpectedEndOfDemo for incomplete / corrupt demos.
May panic if the demo is corrupt in some way.

See also: ParseToEnd() for parsing the complete demo in one go (faster).
*/
func (p *Parser) ParseNextFrame() (moreFrames bool, err error) {
	defer func() {
		// Make sure all the messages of the frame are handled
		p.msgDispatcher.SyncAllQueues()

		// Close msgQueue (only if we are done)
		if p.msgQueue != nil && !moreFrames {
			close(p.msgQueue)
		}

		if err == nil {
			err = recoverFromPanic(recover())
		}
	}()

	if p.header == nil {
		_, err = p.ParseHeader()
		if err != nil {
			return
		}
	}

	moreFrames = p.parseFrame()

	return moreFrames, p.error()
}

// Demo commands as documented at https://developer.valvesoftware.com/wiki/DEM_Format
type demoCommand byte

const (
	dcSignon         demoCommand = 1
	dcPacket         demoCommand = 2
	dcSynctick       demoCommand = 3
	dcConsoleCommand demoCommand = 4
	dcUserCommand    demoCommand = 5
	dcDataTables     demoCommand = 6
	dcStop           demoCommand = 7
	dcCustomData     demoCommand = 8
	dcStringTables   demoCommand = 9
)

func (p *Parser) parseFrame() bool {
	cmd := demoCommand(p.bitReader.ReadSingleByte())

	// Send ingame tick number update
	p.msgQueue <- ingameTickNumber(p.bitReader.ReadSignedInt(32))

	// Skip 'player slot'
	p.bitReader.Skip(8)

	debugDemoCommand(cmd)

	switch cmd {
	case dcSynctick:
		// Ignore

	case dcStop:
		return false

	case dcConsoleCommand:
		// Skip
		p.bitReader.Skip(p.bitReader.ReadSignedInt(32) << 3)

	case dcDataTables:
		p.msgDispatcher.SyncAllQueues()

		p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
		p.stParser.ParsePacket(p.bitReader)
		p.bitReader.EndChunk()

		debugAllServerClasses(p.ServerClasses())

		p.mapEquipment()
		p.bindEntities()

		p.eventDispatcher.Dispatch(events.DataTablesParsed{})

	case dcStringTables:
		p.msgDispatcher.SyncAllQueues()

		p.parseStringTables()

	case dcUserCommand:
		// Skip
		p.bitReader.Skip(32)
		p.bitReader.Skip(p.bitReader.ReadSignedInt(32) << 3)

	case dcSignon:
		fallthrough
	case dcPacket:
		p.parsePacket()

	case dcCustomData:
		// Might as well panic since we'll be way off if we dont skip the whole thing
		panic("Found CustomData but not handled")

	default:
		panic(fmt.Sprintf("I haven't programmed that pathway yet (command %v unknown)", cmd))
	}

	// Queue up some post processing
	p.msgQueue <- frameParsedToken

	return true
}

var byteSlicePool = sync.Pool{
	New: func() interface{} {
		s := make([]byte, 0, 256)
		return &s
	},
}

var defaultNetMessageCreators = map[int]NetMessageCreator{
	// We could pool CSVCMsg_PacketEntities as they take up A LOT of the allocations
	// but unless we're on a system that's doing a lot of concurrent parsing there isn't really a point
	// as handling packets is a lot slower than creating them and we can't pool until they are handled.
	int(msg.SVC_Messages_svc_PacketEntities):    func() proto.Message { return new(msg.CSVCMsg_PacketEntities) },
	int(msg.SVC_Messages_svc_GameEventList):     func() proto.Message { return new(msg.CSVCMsg_GameEventList) },
	int(msg.SVC_Messages_svc_GameEvent):         func() proto.Message { return new(msg.CSVCMsg_GameEvent) },
	int(msg.SVC_Messages_svc_CreateStringTable): func() proto.Message { return new(msg.CSVCMsg_CreateStringTable) },
	int(msg.SVC_Messages_svc_UpdateStringTable): func() proto.Message { return new(msg.CSVCMsg_UpdateStringTable) },
	int(msg.SVC_Messages_svc_UserMessage):       func() proto.Message { return new(msg.CSVCMsg_UserMessage) },
	int(msg.SVC_Messages_svc_ServerInfo):        func() proto.Message { return new(msg.CSVCMsg_ServerInfo) },
	int(msg.NET_Messages_net_SetConVar):         func() proto.Message { return new(msg.CNETMsg_SetConVar) },
}

func (p *Parser) parsePacket() {
	// Booooring
	// 152 bytes CommandInfo, 4 bytes SeqNrIn, 4 bytes SeqNrOut
	// See at the bottom of the file what the CommandInfo would contain if you are interested.
	p.bitReader.Skip((152 + 4 + 4) << 3)

	// Here we go
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)

	for !p.bitReader.ChunkFinished() {
		cmd := int(p.bitReader.ReadVarInt32())
		size := int(p.bitReader.ReadVarInt32())

		p.bitReader.BeginChunk(size << 3)

		msgCreator := defaultNetMessageCreators[cmd]

		if msgCreator == nil {
			var msgName string
			if cmd < 8 || cmd >= 100 {
				msgName = msg.NET_Messages_name[int32(cmd)]
			} else {
				msgName = msg.SVC_Messages_name[int32(cmd)]
			}

			if msgName == "" {
				// Send a warning if the command is unknown
				// This might mean our proto files are out of date
				p.eventDispatcher.Dispatch(events.ParserWarn{Message: fmt.Sprintf("unknown message command %q", cmd)})
				unassert.Error("unknown message command %q", cmd)
			}

			// Handle additional net-messages as defined by the user
			msgCreator = p.additionalNetMessageCreators[cmd]
			if msgCreator == nil {
				debugUnhandledMessage(cmd, msgName)

				// On to the next one
				p.bitReader.EndChunk()
				continue
			}
		}

		b := byteSlicePool.Get().(*[]byte)
		p.bitReader.ReadBytesInto(b, size)

		m := msgCreator()
		if proto.Unmarshal(*b, m) != nil {
			// TODO: Don't crash here, happens with demos that work in gotv
			panic(fmt.Sprintf("Failed to unmarshal cmd %d", cmd))
		}
		p.msgQueue <- m

		// Reset length to 0 and pool
		*b = (*b)[:0]
		byteSlicePool.Put(b)

		p.bitReader.EndChunk()
	}

	p.bitReader.EndChunk()
}

type frameParsedTokenType struct{}

var frameParsedToken = new(frameParsedTokenType)

func (p *Parser) handleFrameParsed(*frameParsedTokenType) {
	// PlayerFlashed events need to be dispatched at the end of the tick
	// because Player.FlashDuration is updated after the game-events are parsed.
	for _, eventHandler := range p.delayedEventHandlers {
		eventHandler()
	}
	p.delayedEventHandlers = p.delayedEventHandlers[:0]

	p.currentFrame++
	p.eventDispatcher.Dispatch(events.TickDone{})
	p.eventDispatcher.Dispatch(events.FrameDone{})
}

/*
Format of 'CommandInfos' - I honestly have no clue what they are good for.
This data is skipped in Parser.parsePacket().
If you find a use for this please let me know!

Here is all i know:

CommandInfo [152 bytes]
- [2]Split

Split [76 bytes]
- flags [4 bytes]
- viewOrigin [12 bytes]
- viewAngles [12 bytes]
- localViewAngles [12 bytes]
- viewOrigin2 [12 bytes]
- viewAngles2 [12 bytes]
- localViewAngles2 [12 bytes]

Origin [12 bytes]
- X [4 bytes]
- Y [4 bytes]
- Z [4 bytes]

Angle [12 bytes]
- X [4 bytes]
- Y [4 bytes]
- Z [4 bytes]

They are parsed in the following order:
split1.flags
split1.viewOrigin.x
split1.viewOrigin.y
split1.viewOrigin.z
split1.viewAngles.x
split1.viewAngles.y
split1.viewAngles.z
split1.localViewAngles.x
split1.localViewAngles.y
split1.localViewAngles.z
split1.viewOrigin2...
split1.viewAngles2...
split1.localViewAngles2...
split2.flags
...

Or just check this file's history for an example on how to parse them
*/
<commit_msg>parsing: force min & max for automatic msgQueue size (#198)<commit_after>package demoinfocs

import (
	"errors"
	"fmt"
	"io"
	"math"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/markus-wa/go-unassert"
	dispatch "github.com/markus-wa/godispatch"

	"github.com/markus-wa/demoinfocs-golang/common"
	"github.com/markus-wa/demoinfocs-golang/events"
	"github.com/markus-wa/demoinfocs-golang/msg"
)

const maxOsPath = 260

const (
	playerWeaponPrefix    = "m_hMyWeapons."
	playerWeaponPrePrefix = "bcc_nonlocaldata."
	gameRulesPrefix       = "cs_gamerules_data"
)

// Parsing errors
var (
	// ErrCancelled signals that parsing was cancelled via Parser.Cancel()
	ErrCancelled = errors.New("parsing was cancelled before it finished (ErrCancelled)")

	// ErrUnexpectedEndOfDemo signals that the demo is incomplete / corrupt -
	// these demos may still be useful, check how far the parser got.
	ErrUnexpectedEndOfDemo = errors.New("demo stream ended unexpectedly (ErrUnexpectedEndOfDemo)")

	// ErrInvalidFileType signals that the input isn't a valid CS:GO demo.
	ErrInvalidFileType = errors.New("invalid File-Type; expecting HL2DEMO in the first 8 bytes (ErrInvalidFileType)")
)

// ParseHeader attempts to parse the header of the demo and returns it.
// If not done manually this will be called by Parser.ParseNextFrame() or Parser.ParseToEnd().
//
// Returns ErrInvalidFileType if the filestamp (first 8 bytes) doesn't match HL2DEMO.
func (p *Parser) ParseHeader() (common.DemoHeader, error) {
	var h common.DemoHeader
	h.Filestamp = p.bitReader.ReadCString(8)
	h.Protocol = p.bitReader.ReadSignedInt(32)
	h.NetworkProtocol = p.bitReader.ReadSignedInt(32)
	h.ServerName = p.bitReader.ReadCString(maxOsPath)
	h.ClientName = p.bitReader.ReadCString(maxOsPath)
	h.MapName = p.bitReader.ReadCString(maxOsPath)
	h.GameDirectory = p.bitReader.ReadCString(maxOsPath)
	h.PlaybackTime = time.Duration(p.bitReader.ReadFloat() * float32(time.Second))
	h.PlaybackTicks = p.bitReader.ReadSignedInt(32)
	h.PlaybackFrames = p.bitReader.ReadSignedInt(32)
	h.SignonLength = p.bitReader.ReadSignedInt(32)

	if h.Filestamp != "HL2DEMO" {
		return h, ErrInvalidFileType
	}

	// Initialize queue if the buffer size wasn't specified, the amount of ticks
	// seems to be a good indicator of how many events we'll get
	if p.msgQueue == nil {
		p.initMsgQueue(msgQueueSize(h.PlaybackTicks))
	}

	p.header = &h

	return h, nil
}

func msgQueueSize(ticks int) int {
	const (
		msgQueueMinSize = 50000
		msgQueueMaxSize = 500000
	)

	size := math.Max(msgQueueMinSize, float64(ticks))
	size = math.Min(msgQueueMaxSize, size)

	return int(size)
}

// ParseToEnd attempts to parse the demo until the end.
// Aborts and returns ErrCancelled if Cancel() is called before the end.
//
// See also: ParseNextFrame() for other possible errors.
func (p *Parser) ParseToEnd() (err error) {
	defer func() {
		// Make sure all the messages of the demo are handled
		p.msgDispatcher.SyncAllQueues()

		// Close msgQueue
		if p.msgQueue != nil {
			close(p.msgQueue)
		}

		if err == nil {
			err = recoverFromPanic(recover())
		}
	}()

	if p.header == nil {
		_, err = p.ParseHeader()
		if err != nil {
			return
		}
	}

	for {
		select {
		case <-p.cancelChan:
			return ErrCancelled

		default:
			if !p.parseFrame() {
				return p.error()
			}
		}

		if err = p.error(); err != nil {
			return
		}
	}
}

func recoverFromPanic(r interface{}) error {
	if r == nil {
		return nil
	}

	if r == io.ErrUnexpectedEOF || r == io.EOF {
		return ErrUnexpectedEndOfDemo
	}

	switch err := r.(type) {
	case dispatch.ConsumerCodePanic:
		panic(err.Value())
	case error:
		return err
	case string:
		return errors.New(err)
	default:
		return fmt.Errorf("unexpected error: %v", err)
	}
}

// Cancel aborts ParseToEnd().
// All information that was already read up to this point may still be used (and new events may still be sent out).
func (p *Parser) Cancel() {
	p.cancelChan <- struct{}{}
}

/*
ParseNextFrame attempts to parse the next frame / demo-tick (not ingame tick).

Returns true unless the demo command 'stop' or an error was encountered.

May return ErrUnexpectedEndOfDemo for incomplete / corrupt demos.
May panic if the demo is corrupt in some way.

See also: ParseToEnd() for parsing the complete demo in one go (faster).
*/
func (p *Parser) ParseNextFrame() (moreFrames bool, err error) {
	defer func() {
		// Make sure all the messages of the frame are handled
		p.msgDispatcher.SyncAllQueues()

		// Close msgQueue (only if we are done)
		if p.msgQueue != nil && !moreFrames {
			close(p.msgQueue)
		}

		if err == nil {
			err = recoverFromPanic(recover())
		}
	}()

	if p.header == nil {
		_, err = p.ParseHeader()
		if err != nil {
			return
		}
	}

	moreFrames = p.parseFrame()

	return moreFrames, p.error()
}

// Demo commands as documented at https://developer.valvesoftware.com/wiki/DEM_Format
type demoCommand byte

const (
	dcSignon         demoCommand = 1
	dcPacket         demoCommand = 2
	dcSynctick       demoCommand = 3
	dcConsoleCommand demoCommand = 4
	dcUserCommand    demoCommand = 5
	dcDataTables     demoCommand = 6
	dcStop           demoCommand = 7
	dcCustomData     demoCommand = 8
	dcStringTables   demoCommand = 9
)

func (p *Parser) parseFrame() bool {
	cmd := demoCommand(p.bitReader.ReadSingleByte())

	// Send ingame tick number update
	p.msgQueue <- ingameTickNumber(p.bitReader.ReadSignedInt(32))

	// Skip 'player slot'
	p.bitReader.Skip(8)

	debugDemoCommand(cmd)

	switch cmd {
	case dcSynctick:
		// Ignore

	case dcStop:
		return false

	case dcConsoleCommand:
		// Skip
		p.bitReader.Skip(p.bitReader.ReadSignedInt(32) << 3)

	case dcDataTables:
		p.msgDispatcher.SyncAllQueues()

		p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
		p.stParser.ParsePacket(p.bitReader)
		p.bitReader.EndChunk()

		debugAllServerClasses(p.ServerClasses())

		p.mapEquipment()
		p.bindEntities()

		p.eventDispatcher.Dispatch(events.DataTablesParsed{})

	case dcStringTables:
		p.msgDispatcher.SyncAllQueues()

		p.parseStringTables()

	case dcUserCommand:
		// Skip
		p.bitReader.Skip(32)
		p.bitReader.Skip(p.bitReader.ReadSignedInt(32) << 3)

	case dcSignon:
		fallthrough
	case dcPacket:
		p.parsePacket()

	case dcCustomData:
		// Might as well panic since we'll be way off if we dont skip the whole thing
		panic("Found CustomData but not handled")

	default:
		panic(fmt.Sprintf("I haven't programmed that pathway yet (command %v unknown)", cmd))
	}

	// Queue up some post processing
	p.msgQueue <- frameParsedToken

	return true
}

var byteSlicePool = sync.Pool{
	New: func() interface{} {
		s := make([]byte, 0, 256)
		return &s
	},
}

var defaultNetMessageCreators = map[int]NetMessageCreator{
	// We could pool CSVCMsg_PacketEntities as they take up A LOT of the allocations
	// but unless we're on a system that's doing a lot of concurrent parsing there isn't really a point
	// as handling packets is a lot slower than creating them and we can't pool until they are handled.
	int(msg.SVC_Messages_svc_PacketEntities):    func() proto.Message { return new(msg.CSVCMsg_PacketEntities) },
	int(msg.SVC_Messages_svc_GameEventList):     func() proto.Message { return new(msg.CSVCMsg_GameEventList) },
	int(msg.SVC_Messages_svc_GameEvent):         func() proto.Message { return new(msg.CSVCMsg_GameEvent) },
	int(msg.SVC_Messages_svc_CreateStringTable): func() proto.Message { return new(msg.CSVCMsg_CreateStringTable) },
	int(msg.SVC_Messages_svc_UpdateStringTable): func() proto.Message { return new(msg.CSVCMsg_UpdateStringTable) },
	int(msg.SVC_Messages_svc_UserMessage):       func() proto.Message { return new(msg.CSVCMsg_UserMessage) },
	int(msg.SVC_Messages_svc_ServerInfo):        func() proto.Message { return new(msg.CSVCMsg_ServerInfo) },
	int(msg.NET_Messages_net_SetConVar):         func() proto.Message { return new(msg.CNETMsg_SetConVar) },
}

func (p *Parser) parsePacket() {
	// Booooring
	// 152 bytes CommandInfo, 4 bytes SeqNrIn, 4 bytes SeqNrOut
	// See at the bottom of the file what the CommandInfo would contain if you are interested.
	p.bitReader.Skip((152 + 4 + 4) << 3)

	// Here we go
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)

	for !p.bitReader.ChunkFinished() {
		cmd := int(p.bitReader.ReadVarInt32())
		size := int(p.bitReader.ReadVarInt32())

		p.bitReader.BeginChunk(size << 3)

		msgCreator := defaultNetMessageCreators[cmd]

		if msgCreator == nil {
			var msgName string
			if cmd < 8 || cmd >= 100 {
				msgName = msg.NET_Messages_name[int32(cmd)]
			} else {
				msgName = msg.SVC_Messages_name[int32(cmd)]
			}

			if msgName == "" {
				// Send a warning if the command is unknown
				// This might mean our proto files are out of date
				p.eventDispatcher.Dispatch(events.ParserWarn{Message: fmt.Sprintf("unknown message command %q", cmd)})
				unassert.Error("unknown message command %q", cmd)
			}

			// Handle additional net-messages as defined by the user
			msgCreator = p.additionalNetMessageCreators[cmd]
			if msgCreator == nil {
				debugUnhandledMessage(cmd, msgName)

				// On to the next one
				p.bitReader.EndChunk()
				continue
			}
		}

		b := byteSlicePool.Get().(*[]byte)
		p.bitReader.ReadBytesInto(b, size)

		m := msgCreator()
		if proto.Unmarshal(*b, m) != nil {
			// TODO: Don't crash here, happens with demos that work in gotv
			panic(fmt.Sprintf("Failed to unmarshal cmd %d", cmd))
		}
		p.msgQueue <- m

		// Reset length to 0 and pool
		*b = (*b)[:0]
		byteSlicePool.Put(b)

		p.bitReader.EndChunk()
	}

	p.bitReader.EndChunk()
}

type frameParsedTokenType struct{}

var frameParsedToken = new(frameParsedTokenType)

func (p *Parser) handleFrameParsed(*frameParsedTokenType) {
	// PlayerFlashed events need to be dispatched at the end of the tick
	// because Player.FlashDuration is updated after the game-events are parsed.
	for _, eventHandler := range p.delayedEventHandlers {
		eventHandler()
	}
	p.delayedEventHandlers = p.delayedEventHandlers[:0]

	p.currentFrame++
	p.eventDispatcher.Dispatch(events.TickDone{})
	p.eventDispatcher.Dispatch(events.FrameDone{})
}

/*
Format of 'CommandInfos' - I honestly have no clue what they are good for.
This data is skipped in Parser.parsePacket().
If you find a use for this please let me know!

Here is all i know:

CommandInfo [152 bytes]
- [2]Split

Split [76 bytes]
- flags [4 bytes]
- viewOrigin [12 bytes]
- viewAngles [12 bytes]
- localViewAngles [12 bytes]
- viewOrigin2 [12 bytes]
- viewAngles2 [12 bytes]
- localViewAngles2 [12 bytes]

Origin [12 bytes]
- X [4 bytes]
- Y [4 bytes]
- Z [4 bytes]

Angle [12 bytes]
- X [4 bytes]
- Y [4 bytes]
- Z [4 bytes]

They are parsed in the following order:
split1.flags
split1.viewOrigin.x
split1.viewOrigin.y
split1.viewOrigin.z
split1.viewAngles.x
split1.viewAngles.y
split1.viewAngles.z
split1.localViewAngles.x
split1.localViewAngles.y
split1.localViewAngles.z
split1.viewOrigin2...
split1.viewAngles2...
split1.localViewAngles2...
split2.flags
...

Or just check this file's history for an example on how to parse them
*/
<|endoftext|>


<commit_before>package genbase

import (
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"strings"

	_ "golang.org/x/tools/go/gcimporter15"
)

var (
	// ErrNotStructType shows argument is not ast.StructType.
	ErrNotStructType = errors.New("type is not ast.StructType")
)

// Parser is center of parsing strategy.
type Parser struct {
	SkipSemanticsCheck bool
}

// PackageInfo is specified package informations.
type PackageInfo struct {
	Dir   string
	Files FileInfos
	Types *types.Package
}

// FileInfo is ast.File synonym.
type FileInfo ast.File

// FileInfos is []*FileInfo synonym.
type FileInfos []*FileInfo

// TypeInfo is type information gathering.
// try http://goast.yuroyoro.net/ with http://play.golang.org/p/ruqMMsbDaw
type TypeInfo struct {
	FileInfo         *FileInfo
	GenDecl          *ast.GenDecl
	TypeSpec         *ast.TypeSpec
	AnnotatedComment *ast.Comment
}

// TypeInfos is []*TypeInfo synonym.
type TypeInfos []*TypeInfo

// StructTypeInfo is ast.StructType synonym.
type StructTypeInfo ast.StructType

// FieldInfo is ast.Field synonym.
type FieldInfo ast.Field

// FieldInfos is []*FieldInfo synonym.
type FieldInfos []*FieldInfo

// ParsePackageDir parses specified directory.
func (p *Parser) ParsePackageDir(directory string) (*PackageInfo, error) {
	pkg, err := build.Default.ImportDir(directory, 0)
	if err != nil {
		return nil, fmt.Errorf("cannot process directory %s: %s", directory, err)
	}
	var names []string
	names = append(names, pkg.GoFiles...)
	names = append(names, pkg.CgoFiles...)
	names = append(names, pkg.SFiles...)
	names = pathJoinAll(directory, names...)
	return p.parsePackage(directory, names, nil)
}

// ParsePackageFiles parses specified files.
func (p *Parser) ParsePackageFiles(fileNames []string) (*PackageInfo, error) {
	return p.parsePackage(".", fileNames, nil)
}

func (p *Parser) ParseStringSource(fileName string, code string) (*PackageInfo, error) {
	return p.parsePackage(".", []string{fileName}, []string{code})
}

func (p *Parser) parsePackage(directory string, fileNames []string, codes []string) (*PackageInfo, error) {
	var files FileInfos
	pkg := &PackageInfo{}
	fs := token.NewFileSet()
	for idx, fileName := range fileNames {
		if !strings.HasSuffix(fileName, ".go") {
			continue
		}
		var code interface{}
		if idx < len(codes) {
			code = codes[idx]
		}
		parsedFile, err := parser.ParseFile(fs, fileName, code, parser.ParseComments)
		if err != nil {
			return nil, fmt.Errorf("parsing package: %s: %s", fileName, err)
		}
		files = append(files, (*FileInfo)(parsedFile))
	}
	if len(files) == 0 {
		return nil, fmt.Errorf("%s: no buildable Go files", directory)
	}
	pkg.Files = files
	pkg.Dir = directory

	// resolve types
	config := types.Config{
		FakeImportC:              true,
		IgnoreFuncBodies:         true,
		DisableUnusedImportCheck: true,
	}
	info := &types.Info{
		Defs: make(map[*ast.Ident]types.Object),
	}
	typesPkg, err := config.Check(pkg.Dir, fs, files.AstFiles(), info)
	if p.SkipSemanticsCheck && err != nil {
		return pkg, nil
	} else if err != nil {
		return nil, err
	}
	pkg.Types = typesPkg

	return pkg, nil
}

// TypeInfos is gathering TypeInfos, it included in package.
func (pkg *PackageInfo) TypeInfos() TypeInfos {
	var types TypeInfos
	for _, file := range pkg.Files {
		if file == nil {
			continue
		}
		ast.Inspect(file.AstFile(), func(node ast.Node) bool {
			decl, ok := node.(*ast.GenDecl)
			if !ok || decl.Tok != token.TYPE {
				return true
			}
			found := false
			for _, spec := range decl.Specs {
				ts, ok := spec.(*ast.TypeSpec)
				if !ok {
					continue
				}
				types = append(types, &TypeInfo{
					FileInfo: file,
					GenDecl:  decl,
					TypeSpec: ts,
				})
				found = true
			}
			return !found
		})
	}
	return types
}

// CollectTaggedTypeInfos collects tagged TypeInfos.
func (pkg *PackageInfo) CollectTaggedTypeInfos(tag string) TypeInfos {
	ret := TypeInfos{}

	types := pkg.TypeInfos()

	for _, t := range types {
		if c := findAnnotation(t.Doc(), tag); c != nil {
			t.AnnotatedComment = c
			ret = append(ret, t)
		}
	}

	return ret
}

// CollectTypeInfos collects specified TypeInfos.
func (pkg *PackageInfo) CollectTypeInfos(typeNames []string) TypeInfos {
	ret := TypeInfos{}

	types := pkg.TypeInfos()

outer:
	for _, t := range types {
		for _, name := range typeNames {
			if t.Name() == name {
				ret = append(ret, t)
				continue outer
			}
		}
	}

	return ret
}

// Name returns package name.
func (pkg *PackageInfo) Name() string {
	return pkg.Files[0].Name.Name
}

// AstFile returns *ast.File.
func (file *FileInfo) AstFile() *ast.File {
	return (*ast.File)(file)
}

// AstFiles returns []*ast.File.
func (files FileInfos) AstFiles() []*ast.File {
	astFiles := make([]*ast.File, len(files))
	for i, file := range files {
		astFiles[i] = file.AstFile()
	}
	return astFiles
}

// FindImportSpecByIdent finds *ast.ImportSpec by package ident.
func (file *FileInfo) FindImportSpecByIdent(packageIdent string) *ast.ImportSpec {
	for _, imp := range file.Imports {
		if imp.Name != nil && imp.Name.Name == packageIdent {
			// import foo "foobar"
			return imp
		} else if strings.HasSuffix(imp.Path.Value, fmt.Sprintf(`/%s"`, packageIdent)) {
			// import "favclip/foo"
			return imp
		} else if imp.Path.Value == fmt.Sprintf(`"%s"`, packageIdent) {
			// import "foo"
			return imp
		}
	}
	return nil
}

// StructType returns *StructTypeInfo.
func (t *TypeInfo) StructType() (*StructTypeInfo, error) {
	structType, ok := interface{}(t.TypeSpec.Type).(*ast.StructType)
	if !ok {
		return nil, ErrNotStructType
	}

	return (*StructTypeInfo)(structType), nil
}

// Name return type name.
func (t *TypeInfo) Name() string {
	return t.TypeSpec.Name.Name
}

// Doc returns *ast.CommentGroup of TypeInfo.
func (t *TypeInfo) Doc() *ast.CommentGroup {
	if t.TypeSpec.Doc != nil {
		return t.TypeSpec.Doc
	}
	if t.GenDecl.Doc != nil {
		return t.GenDecl.Doc
	}
	return nil
}

// AstStructType returns *ast.StructType.
func (st *StructTypeInfo) AstStructType() *ast.StructType {
	return (*ast.StructType)(st)
}

// FieldInfos returns FieldInfos of struct.
func (st *StructTypeInfo) FieldInfos() FieldInfos {
	var fields FieldInfos
	for _, field := range st.AstStructType().Fields.List {
		fields = append(fields, (*FieldInfo)(field))
	}

	return fields
}

// TypeName returns type name of field.
func (f *FieldInfo) TypeName() string {
	typeName, err := ExprToTypeName(f.Type)
	if err != nil {
		return fmt.Sprintf("!!%s!!", err.Error())
	}
	return typeName
}

// IsPtr returns true if FieldInfo is pointer, otherwise returns false.
func (f *FieldInfo) IsPtr() bool {
	_, ok := f.Type.(*ast.StarExpr)
	return ok
}

// IsArray returns true if FieldInfo is array, otherwise returns false.
func (f *FieldInfo) IsArray() bool {
	_, ok := f.Type.(*ast.ArrayType)
	return ok
}

// IsPtrArray returns true if FieldInfo is pointer array, otherwise returns false.
func (f *FieldInfo) IsPtrArray() bool {
	star, ok := f.Type.(*ast.StarExpr)
	if !ok {
		return false
	}
	_, ok = star.X.(*ast.ArrayType)
	return ok
}

// IsArrayPtr returns true if FieldInfo is pointer of array, otherwise returns false.
func (f *FieldInfo) IsArrayPtr() bool {
	array, ok := f.Type.(*ast.ArrayType)
	if !ok {
		return false
	}
	_, ok = array.Elt.(*ast.StarExpr)
	return ok
}

// IsPtrArrayPtr returns true if FieldInfo is pointer of pointer array, otherwise returns false.
func (f *FieldInfo) IsPtrArrayPtr() bool {
	star, ok := f.Type.(*ast.StarExpr)
	if !ok {
		return false
	}
	array, ok := star.X.(*ast.ArrayType)
	if !ok {
		return false
	}
	_, ok = array.Elt.(*ast.StarExpr)
	return ok
}

// IsInt64 returns true if FieldInfo is int64, otherwise returns false.
func (f *FieldInfo) IsInt64() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "int64"
}

// IsInt returns true if FieldInfo is int, otherwise returns false.
func (f *FieldInfo) IsInt() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "int"
}

// IsString returns true if FieldInfo is string, otherwise returns false.
func (f *FieldInfo) IsString() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "string"
}

// IsFloat32 returns true if FieldInfo is float32, otherwise returns false.
func (f *FieldInfo) IsFloat32() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "float32"
}

// IsFloat64 returns true if FieldInfo is float64, otherwise returns false.
func (f *FieldInfo) IsFloat64() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "float64"
}

// IsNumber returns true if FieldInfo is int or int64 or float32 or float64, otherwise returns false.
func (f *FieldInfo) IsNumber() bool {
	return f.IsInt() || f.IsInt64() || f.IsFloat32() || f.IsFloat64()
}

// IsBool returns true if FieldInfo is bool, otherwise returns false.
func (f *FieldInfo) IsBool() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "bool"
}

// IsTime returns true if FieldInfo is time.Time, otherwise returns false.
func (f *FieldInfo) IsTime() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "time.Time"
}
<commit_msg>fixed issue https://github.com/favclip/jwg/issues/3<commit_after>package genbase

import (
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"strings"
)

var (
	// ErrNotStructType shows argument is not ast.StructType.
	ErrNotStructType = errors.New("type is not ast.StructType")
)

// Parser is center of parsing strategy.
type Parser struct {
	SkipSemanticsCheck bool
}

// PackageInfo is specified package informations.
type PackageInfo struct {
	Dir   string
	Files FileInfos
	Types *types.Package
}

// FileInfo is ast.File synonym.
type FileInfo ast.File

// FileInfos is []*FileInfo synonym.
type FileInfos []*FileInfo

// TypeInfo is type information gathering.
// try http://goast.yuroyoro.net/ with http://play.golang.org/p/ruqMMsbDaw
type TypeInfo struct {
	FileInfo         *FileInfo
	GenDecl          *ast.GenDecl
	TypeSpec         *ast.TypeSpec
	AnnotatedComment *ast.Comment
}

// TypeInfos is []*TypeInfo synonym.
type TypeInfos []*TypeInfo

// StructTypeInfo is ast.StructType synonym.
type StructTypeInfo ast.StructType

// FieldInfo is ast.Field synonym.
type FieldInfo ast.Field

// FieldInfos is []*FieldInfo synonym.
type FieldInfos []*FieldInfo

// ParsePackageDir parses specified directory.
func (p *Parser) ParsePackageDir(directory string) (*PackageInfo, error) {
	pkg, err := build.Default.ImportDir(directory, 0)
	if err != nil {
		return nil, fmt.Errorf("cannot process directory %s: %s", directory, err)
	}
	var names []string
	names = append(names, pkg.GoFiles...)
	names = append(names, pkg.CgoFiles...)
	names = append(names, pkg.SFiles...)
	names = pathJoinAll(directory, names...)
	return p.parsePackage(directory, names, nil)
}

// ParsePackageFiles parses specified files.
func (p *Parser) ParsePackageFiles(fileNames []string) (*PackageInfo, error) {
	return p.parsePackage(".", fileNames, nil)
}

func (p *Parser) ParseStringSource(fileName string, code string) (*PackageInfo, error) {
	return p.parsePackage(".", []string{fileName}, []string{code})
}

func (p *Parser) parsePackage(directory string, fileNames []string, codes []string) (*PackageInfo, error) {
	var files FileInfos
	pkg := &PackageInfo{}
	fs := token.NewFileSet()
	for idx, fileName := range fileNames {
		if !strings.HasSuffix(fileName, ".go") {
			continue
		}
		var code interface{}
		if idx < len(codes) {
			code = codes[idx]
		}
		parsedFile, err := parser.ParseFile(fs, fileName, code, parser.ParseComments)
		if err != nil {
			return nil, fmt.Errorf("parsing package: %s: %s", fileName, err)
		}
		files = append(files, (*FileInfo)(parsedFile))
	}
	if len(files) == 0 {
		return nil, fmt.Errorf("%s: no buildable Go files", directory)
	}
	pkg.Files = files
	pkg.Dir = directory

	// resolve types
	config := types.Config{
		FakeImportC:              true,
		Importer:                 importer.Default(),
		IgnoreFuncBodies:         true,
		DisableUnusedImportCheck: true,
	}
	info := &types.Info{
		Defs: make(map[*ast.Ident]types.Object),
	}
	typesPkg, err := config.Check(pkg.Dir, fs, files.AstFiles(), info)
	if p.SkipSemanticsCheck && err != nil {
		return pkg, nil
	} else if err != nil {
		return nil, err
	}
	pkg.Types = typesPkg

	return pkg, nil
}

// TypeInfos is gathering TypeInfos, it included in package.
func (pkg *PackageInfo) TypeInfos() TypeInfos {
	var types TypeInfos
	for _, file := range pkg.Files {
		if file == nil {
			continue
		}
		ast.Inspect(file.AstFile(), func(node ast.Node) bool {
			decl, ok := node.(*ast.GenDecl)
			if !ok || decl.Tok != token.TYPE {
				return true
			}
			found := false
			for _, spec := range decl.Specs {
				ts, ok := spec.(*ast.TypeSpec)
				if !ok {
					continue
				}
				types = append(types, &TypeInfo{
					FileInfo: file,
					GenDecl:  decl,
					TypeSpec: ts,
				})
				found = true
			}
			return !found
		})
	}
	return types
}

// CollectTaggedTypeInfos collects tagged TypeInfos.
func (pkg *PackageInfo) CollectTaggedTypeInfos(tag string) TypeInfos {
	ret := TypeInfos{}

	types := pkg.TypeInfos()

	for _, t := range types {
		if c := findAnnotation(t.Doc(), tag); c != nil {
			t.AnnotatedComment = c
			ret = append(ret, t)
		}
	}

	return ret
}

// CollectTypeInfos collects specified TypeInfos.
func (pkg *PackageInfo) CollectTypeInfos(typeNames []string) TypeInfos {
	ret := TypeInfos{}

	types := pkg.TypeInfos()

outer:
	for _, t := range types {
		for _, name := range typeNames {
			if t.Name() == name {
				ret = append(ret, t)
				continue outer
			}
		}
	}

	return ret
}

// Name returns package name.
func (pkg *PackageInfo) Name() string {
	return pkg.Files[0].Name.Name
}

// AstFile returns *ast.File.
func (file *FileInfo) AstFile() *ast.File {
	return (*ast.File)(file)
}

// AstFiles returns []*ast.File.
func (files FileInfos) AstFiles() []*ast.File {
	astFiles := make([]*ast.File, len(files))
	for i, file := range files {
		astFiles[i] = file.AstFile()
	}
	return astFiles
}

// FindImportSpecByIdent finds *ast.ImportSpec by package ident.
func (file *FileInfo) FindImportSpecByIdent(packageIdent string) *ast.ImportSpec {
	for _, imp := range file.Imports {
		if imp.Name != nil && imp.Name.Name == packageIdent {
			// import foo "foobar"
			return imp
		} else if strings.HasSuffix(imp.Path.Value, fmt.Sprintf(`/%s"`, packageIdent)) {
			// import "favclip/foo"
			return imp
		} else if imp.Path.Value == fmt.Sprintf(`"%s"`, packageIdent) {
			// import "foo"
			return imp
		}
	}
	return nil
}

// StructType returns *StructTypeInfo.
func (t *TypeInfo) StructType() (*StructTypeInfo, error) {
	structType, ok := interface{}(t.TypeSpec.Type).(*ast.StructType)
	if !ok {
		return nil, ErrNotStructType
	}

	return (*StructTypeInfo)(structType), nil
}

// Name return type name.
func (t *TypeInfo) Name() string {
	return t.TypeSpec.Name.Name
}

// Doc returns *ast.CommentGroup of TypeInfo.
func (t *TypeInfo) Doc() *ast.CommentGroup {
	if t.TypeSpec.Doc != nil {
		return t.TypeSpec.Doc
	}
	if t.GenDecl.Doc != nil {
		return t.GenDecl.Doc
	}
	return nil
}

// AstStructType returns *ast.StructType.
func (st *StructTypeInfo) AstStructType() *ast.StructType {
	return (*ast.StructType)(st)
}

// FieldInfos returns FieldInfos of struct.
func (st *StructTypeInfo) FieldInfos() FieldInfos {
	var fields FieldInfos
	for _, field := range st.AstStructType().Fields.List {
		fields = append(fields, (*FieldInfo)(field))
	}

	return fields
}

// TypeName returns type name of field.
func (f *FieldInfo) TypeName() string {
	typeName, err := ExprToTypeName(f.Type)
	if err != nil {
		return fmt.Sprintf("!!%s!!", err.Error())
	}
	return typeName
}

// IsPtr returns true if FieldInfo is pointer, otherwise returns false.
func (f *FieldInfo) IsPtr() bool {
	_, ok := f.Type.(*ast.StarExpr)
	return ok
}

// IsArray returns true if FieldInfo is array, otherwise returns false.
func (f *FieldInfo) IsArray() bool {
	_, ok := f.Type.(*ast.ArrayType)
	return ok
}

// IsPtrArray returns true if FieldInfo is pointer array, otherwise returns false.
func (f *FieldInfo) IsPtrArray() bool {
	star, ok := f.Type.(*ast.StarExpr)
	if !ok {
		return false
	}
	_, ok = star.X.(*ast.ArrayType)
	return ok
}

// IsArrayPtr returns true if FieldInfo is pointer of array, otherwise returns false.
func (f *FieldInfo) IsArrayPtr() bool {
	array, ok := f.Type.(*ast.ArrayType)
	if !ok {
		return false
	}
	_, ok = array.Elt.(*ast.StarExpr)
	return ok
}

// IsPtrArrayPtr returns true if FieldInfo is pointer of pointer array, otherwise returns false.
func (f *FieldInfo) IsPtrArrayPtr() bool {
	star, ok := f.Type.(*ast.StarExpr)
	if !ok {
		return false
	}
	array, ok := star.X.(*ast.ArrayType)
	if !ok {
		return false
	}
	_, ok = array.Elt.(*ast.StarExpr)
	return ok
}

// IsInt64 returns true if FieldInfo is int64, otherwise returns false.
func (f *FieldInfo) IsInt64() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "int64"
}

// IsInt returns true if FieldInfo is int, otherwise returns false.
func (f *FieldInfo) IsInt() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "int"
}

// IsString returns true if FieldInfo is string, otherwise returns false.
func (f *FieldInfo) IsString() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "string"
}

// IsFloat32 returns true if FieldInfo is float32, otherwise returns false.
func (f *FieldInfo) IsFloat32() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "float32"
}

// IsFloat64 returns true if FieldInfo is float64, otherwise returns false.
func (f *FieldInfo) IsFloat64() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "float64"
}

// IsNumber returns true if FieldInfo is int or int64 or float32 or float64, otherwise returns false.
func (f *FieldInfo) IsNumber() bool {
	return f.IsInt() || f.IsInt64() || f.IsFloat32() || f.IsFloat64()
}

// IsBool returns true if FieldInfo is bool, otherwise returns false.
func (f *FieldInfo) IsBool() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "bool"
}

// IsTime returns true if FieldInfo is time.Time, otherwise returns false.
func (f *FieldInfo) IsTime() bool {
	typeName, err := ExprToBaseTypeName(f.Type)
	if err != nil {
		return false
	}
	return typeName == "time.Time"
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package model

import (
	"fmt"
	"strconv"

	"github.com/opentracing/opentracing-go/ext"
)

// TraceID is a random 128bit identifier for a trace
type TraceID struct {
	Low  uint64 `json:"lo"`
	High uint64 `json:"hi"`
}

// SpanID is a random 64bit identifier for a span
type SpanID uint64

// Span represents a unit of work in an application, such as an RPC, a database call, etc.
type Span struct {
	TraceID       TraceID   `json:"traceId"`
	SpanID        SpanID    `json:"spanId"`
	ParentSpanID  SpanID    `json:"parentSpanId"`
	OperationName string    `json:"operationName"`
	References    []SpanRef `json:"references,omitempty"`
	Flags         uint32    `json:"flags"`
	StartTime     uint64    `json:"startTime"`
	Duration      uint64    `json:"duration"`
	Tags          KeyValues `json:"tags,omitempty"`
	Logs          []Log     `json:"logs,omitempty"`
	Process       *Process  `json:"process"`
}

// HasSpanKind returns true if the span has a `span.kind` tag set to `kind`.
func (s *Span) HasSpanKind(kind ext.SpanKindEnum) bool {
	if tag, ok := s.Tags.FindByKey(string(ext.SpanKind)); ok {
		return tag.AsString() == string(kind)
	}
	return false
}

// IsRPCClient returns true if the span represents a client side of an RPC,
// as indicated by the `span.kind` tag set to `client`.
func (s *Span) IsRPCClient() bool {
	return s.HasSpanKind(ext.SpanKindRPCClientEnum)
}

// IsRPCServer returns true if the span represents a server side of an RPC,
// as indicated by the `span.kind` tag set to `server`.
func (s *Span) IsRPCServer() bool {
	return s.HasSpanKind(ext.SpanKindRPCServerEnum)
}

// ------- TraceID -------

func (t TraceID) String() string {
	if t.High == 0 {
		return fmt.Sprintf("%x", t.Low)
	}
	return fmt.Sprintf("%x%016x", t.High, t.Low)
}

// TraceIDFromString creates a TraceID from a hexadecimal string
func TraceIDFromString(s string) (TraceID, error) {
	var hi, lo uint64
	var err error
	if len(s) > 32 {
		return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
	} else if len(s) > 16 {
		hiLen := len(s) - 16
		if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
			return TraceID{}, err
		}
		if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
			return TraceID{}, err
		}
	} else {
		if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
			return TraceID{}, err
		}
	}
	return TraceID{High: hi, Low: lo}, nil
}

// MarshalText allows TraceID to serialize itself in JSON as a string.
func (t TraceID) MarshalText() ([]byte, error) {
	return []byte(t.String()), nil
}

// UnmarshalText allows TraceID to deserialize itself from a JSON string.
func (t *TraceID) UnmarshalText(text []byte) error {
	q, err := TraceIDFromString(string(text))
	if err != nil {
		return err
	}
	*t = q
	return nil
}

// ------- SpanID -------

func (s SpanID) String() string {
	return fmt.Sprintf("%x", uint64(s))
}

// SpanIDFromString creates a SpanID from a hexadecimal string
func SpanIDFromString(s string) (SpanID, error) {
	if len(s) > 16 {
		return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
	}
	id, err := strconv.ParseUint(s, 16, 64)
	if err != nil {
		return SpanID(0), err
	}
	return SpanID(id), nil
}

// MarshalText allows SpanID to serialize itself in JSON as a string.
func (s SpanID) MarshalText() ([]byte, error) {
	return []byte(s.String()), nil
}

// UnmarshalText allows SpanID to deserialize itself from a JSON string.
func (s *SpanID) UnmarshalText(text []byte) error {
	q, err := SpanIDFromString(string(text))
	if err != nil {
		return err
	}
	*s = q
	return nil
}
<commit_msg>Change field names for IDs to use upper-case "ID", e.g. "traceID"<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package model

import (
	"fmt"
	"strconv"

	"github.com/opentracing/opentracing-go/ext"
)

// TraceID is a random 128bit identifier for a trace
type TraceID struct {
	Low  uint64 `json:"lo"`
	High uint64 `json:"hi"`
}

// SpanID is a random 64bit identifier for a span
type SpanID uint64

// Span represents a unit of work in an application, such as an RPC, a database call, etc.
type Span struct {
	TraceID       TraceID   `json:"traceID"`
	SpanID        SpanID    `json:"spanID"`
	ParentSpanID  SpanID    `json:"parentSpanID"`
	OperationName string    `json:"operationName"`
	References    []SpanRef `json:"references,omitempty"`
	Flags         uint32    `json:"flags"`
	StartTime     uint64    `json:"startTime"`
	Duration      uint64    `json:"duration"`
	Tags          KeyValues `json:"tags,omitempty"`
	Logs          []Log     `json:"logs,omitempty"`
	Process       *Process  `json:"process"`
}

// HasSpanKind returns true if the span has a `span.kind` tag set to `kind`.
func (s *Span) HasSpanKind(kind ext.SpanKindEnum) bool {
	if tag, ok := s.Tags.FindByKey(string(ext.SpanKind)); ok {
		return tag.AsString() == string(kind)
	}
	return false
}

// IsRPCClient returns true if the span represents a client side of an RPC,
// as indicated by the `span.kind` tag set to `client`.
func (s *Span) IsRPCClient() bool {
	return s.HasSpanKind(ext.SpanKindRPCClientEnum)
}

// IsRPCServer returns true if the span represents a server side of an RPC,
// as indicated by the `span.kind` tag set to `server`.
func (s *Span) IsRPCServer() bool {
	return s.HasSpanKind(ext.SpanKindRPCServerEnum)
}

// ------- TraceID -------

func (t TraceID) String() string {
	if t.High == 0 {
		return fmt.Sprintf("%x", t.Low)
	}
	return fmt.Sprintf("%x%016x", t.High, t.Low)
}

// TraceIDFromString creates a TraceID from a hexadecimal string
func TraceIDFromString(s string) (TraceID, error) {
	var hi, lo uint64
	var err error
	if len(s) > 32 {
		return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
	} else if len(s) > 16 {
		hiLen := len(s) - 16
		if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
			return TraceID{}, err
		}
		if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
			return TraceID{}, err
		}
	} else {
		if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
			return TraceID{}, err
		}
	}
	return TraceID{High: hi, Low: lo}, nil
}

// MarshalText allows TraceID to serialize itself in JSON as a string.
func (t TraceID) MarshalText() ([]byte, error) {
	return []byte(t.String()), nil
}

// UnmarshalText allows TraceID to deserialize itself from a JSON string.
func (t *TraceID) UnmarshalText(text []byte) error {
	q, err := TraceIDFromString(string(text))
	if err != nil {
		return err
	}
	*t = q
	return nil
}

// ------- SpanID -------

func (s SpanID) String() string {
	return fmt.Sprintf("%x", uint64(s))
}

// SpanIDFromString creates a SpanID from a hexadecimal string
func SpanIDFromString(s string) (SpanID, error) {
	if len(s) > 16 {
		return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
	}
	id, err := strconv.ParseUint(s, 16, 64)
	if err != nil {
		return SpanID(0), err
	}
	return SpanID(id), nil
}

// MarshalText allows SpanID to serialize itself in JSON as a string.
func (s SpanID) MarshalText() ([]byte, error) {
	return []byte(s.String()), nil
}

// UnmarshalText allows SpanID to deserialize itself from a JSON string.
func (s *SpanID) UnmarshalText(text []byte) error {
	q, err := SpanIDFromString(string(text))
	if err != nil {
		return err
	}
	*s = q
	return nil
}
<|endoftext|>


<commit_before>package graphdriver

import (
	"errors"
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
)

type FsMagic uint32

const (
	FsMagicBtrfs       = FsMagic(0x9123683E)
	FsMagicAufs        = FsMagic(0x61756673)
	FsMagicExtfs       = FsMagic(0x0000EF53)
	FsMagicCramfs      = FsMagic(0x28cd3d45)
	FsMagicRamFs       = FsMagic(0x858458f6)
	FsMagicTmpFs       = FsMagic(0x01021994)
	FsMagicSquashFs    = FsMagic(0x73717368)
	FsMagicNfsFs       = FsMagic(0x00006969)
	FsMagicReiserFs    = FsMagic(0x52654973)
	FsMagicSmbFs       = FsMagic(0x0000517B)
	FsMagicJffs2Fs     = FsMagic(0x000072b6)
	FsMagicZfs         = FsMagic(0x2fc12fc1)
	FsMagicXfs         = FsMagic(0x58465342)
	FsMagicUnsupported = FsMagic(0x00000000)
)

var (
	DefaultDriver string
	// All registred drivers
	drivers map[string]InitFunc
	// Slice of drivers that should be used in an order
	priority = []string{
		"aufs",
		"btrfs",
		"devicemapper",
		"vfs",
		// experimental, has to be enabled manually for now
		"overlay",
	}

	ErrNotSupported   = errors.New("driver not supported")
	ErrPrerequisites  = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
	ErrIncompatibleFS = fmt.Errorf("backing file system is unsupported for this graph driver")

	FsNames = map[FsMagic]string{
		FsMagicAufs:        "aufs",
		FsMagicBtrfs:       "btrfs",
		FsMagicExtfs:       "extfs",
		FsMagicCramfs:      "cramfs",
		FsMagicRamFs:       "ramfs",
		FsMagicTmpFs:       "tmpfs",
		FsMagicSquashFs:    "squashfs",
		FsMagicNfsFs:       "nfs",
		FsMagicReiserFs:    "reiserfs",
		FsMagicSmbFs:       "smb",
		FsMagicJffs2Fs:     "jffs2",
		FsMagicZfs:         "zfs",
		FsMagicXfs:         "xfs",
		FsMagicUnsupported: "unsupported",
	}
)

type InitFunc func(root string, options []string) (Driver, error)

// ProtoDriver defines the basic capabilities of a driver.
// This interface exists solely to be a minimum set of methods
// for client code which choose not to implement the entire Driver
// interface and use the NaiveDiffDriver wrapper constructor.
//
// Use of ProtoDriver directly by client code is not recommended.
type ProtoDriver interface {
	// String returns a string representation of this driver.
	String() string
	// Create creates a new, empty, filesystem layer with the
	// specified id and parent. Parent may be "".
	Create(id, parent string) error
	// Remove attempts to remove the filesystem layer with this id.
	Remove(id string) error
	// Get returns the mountpoint for the layered filesystem referred
	// to by this id. You can optionally specify a mountLabel or "".
	// Returns the absolute path to the mounted layered filesystem.
	Get(id, mountLabel string) (dir string, err error)
	// Put releases the system resources for the specified id,
	// e.g, unmounting layered filesystem.
	Put(id string) error
	// Exists returns whether a filesystem layer with the specified
	// ID exists on this driver.
	Exists(id string) bool
	// Status returns a set of key-value pairs which give low
	// level diagnostic status about this driver.
	Status() [][2]string
	// Cleanup performs necessary tasks to release resources
	// held by the driver, e.g., unmounting all layered filesystems
	// known to this driver.
	Cleanup() error
}

// Driver is the interface for layered/snapshot file system drivers.
type Driver interface {
	ProtoDriver
	// Diff produces an archive of the changes between the specified
	// layer and its parent layer which may be "".
	Diff(id, parent string) (archive.Archive, error)
	// Changes produces a list of changes between the specified layer
	// and its parent layer. If parent is "", then all changes will be ADD changes.
	Changes(id, parent string) ([]archive.Change, error)
	// ApplyDiff extracts the changeset from the given diff into the
	// layer with the specified id and parent, returning the size of the
	// new layer in bytes.
	ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
	// DiffSize calculates the changes between the specified id
	// and its parent and returns the size in bytes of the changes
	// relative to its base filesystem directory.
	DiffSize(id, parent string) (size int64, err error)
}

func init() {
	drivers = make(map[string]InitFunc)
}

func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

func GetDriver(name, home string, options []string) (Driver, error) {
	if initFunc, exists := drivers[name]; exists {
		return initFunc(path.Join(home, name), options)
	}
	return nil, ErrNotSupported
}

func New(root string, options []string) (driver Driver, err error) {
	for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
		if name != "" {
			return GetDriver(name, root, options)
		}
	}

	// Check for priority drivers first
	for _, name := range priority {
		driver, err = GetDriver(name, root, options)
		if err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}

	// Check all registered drivers if no priority driver is found
	for name, initFunc := range drivers {
		if driver, err = initFunc(root, options); err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}
	return nil, fmt.Errorf("No supported storage backend found")
}

func checkPriorDriver(name, root string) {
	priorDrivers := []string{}
	for prior := range drivers {
		if prior != name && prior != "vfs" {
			if _, err := os.Stat(path.Join(root, prior)); err == nil {
				priorDrivers = append(priorDrivers, prior)
			}
		}
	}
	if len(priorDrivers) > 0 {
		logrus.Warnf("Graphdriver %s selected. Your graphdriver directory %s already contains data managed by other graphdrivers: %s", name, root, strings.Join(priorDrivers, ","))
	}
}
<commit_msg>graphdriver: promote overlay above vfs<commit_after>package graphdriver

import (
	"errors"
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
)

type FsMagic uint32

const (
	FsMagicBtrfs       = FsMagic(0x9123683E)
	FsMagicAufs        = FsMagic(0x61756673)
	FsMagicExtfs       = FsMagic(0x0000EF53)
	FsMagicCramfs      = FsMagic(0x28cd3d45)
	FsMagicRamFs       = FsMagic(0x858458f6)
	FsMagicTmpFs       = FsMagic(0x01021994)
	FsMagicSquashFs    = FsMagic(0x73717368)
	FsMagicNfsFs       = FsMagic(0x00006969)
	FsMagicReiserFs    = FsMagic(0x52654973)
	FsMagicSmbFs       = FsMagic(0x0000517B)
	FsMagicJffs2Fs     = FsMagic(0x000072b6)
	FsMagicZfs         = FsMagic(0x2fc12fc1)
	FsMagicXfs         = FsMagic(0x58465342)
	FsMagicUnsupported = FsMagic(0x00000000)
)

var (
	DefaultDriver string
	// All registred drivers
	drivers map[string]InitFunc
	// Slice of drivers that should be used in an order
	priority = []string{
		"aufs",
		"btrfs",
		"devicemapper",
		"overlay",
		"vfs",
	}

	ErrNotSupported   = errors.New("driver not supported")
	ErrPrerequisites  = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
	ErrIncompatibleFS = fmt.Errorf("backing file system is unsupported for this graph driver")

	FsNames = map[FsMagic]string{
		FsMagicAufs:        "aufs",
		FsMagicBtrfs:       "btrfs",
		FsMagicExtfs:       "extfs",
		FsMagicCramfs:      "cramfs",
		FsMagicRamFs:       "ramfs",
		FsMagicTmpFs:       "tmpfs",
		FsMagicSquashFs:    "squashfs",
		FsMagicNfsFs:       "nfs",
		FsMagicReiserFs:    "reiserfs",
		FsMagicSmbFs:       "smb",
		FsMagicJffs2Fs:     "jffs2",
		FsMagicZfs:         "zfs",
		FsMagicXfs:         "xfs",
		FsMagicUnsupported: "unsupported",
	}
)

type InitFunc func(root string, options []string) (Driver, error)

// ProtoDriver defines the basic capabilities of a driver.
// This interface exists solely to be a minimum set of methods
// for client code which choose not to implement the entire Driver
// interface and use the NaiveDiffDriver wrapper constructor.
//
// Use of ProtoDriver directly by client code is not recommended.
type ProtoDriver interface {
	// String returns a string representation of this driver.
	String() string
	// Create creates a new, empty, filesystem layer with the
	// specified id and parent. Parent may be "".
	Create(id, parent string) error
	// Remove attempts to remove the filesystem layer with this id.
	Remove(id string) error
	// Get returns the mountpoint for the layered filesystem referred
	// to by this id. You can optionally specify a mountLabel or "".
	// Returns the absolute path to the mounted layered filesystem.
	Get(id, mountLabel string) (dir string, err error)
	// Put releases the system resources for the specified id,
	// e.g, unmounting layered filesystem.
	Put(id string) error
	// Exists returns whether a filesystem layer with the specified
	// ID exists on this driver.
	Exists(id string) bool
	// Status returns a set of key-value pairs which give low
	// level diagnostic status about this driver.
	Status() [][2]string
	// Cleanup performs necessary tasks to release resources
	// held by the driver, e.g., unmounting all layered filesystems
	// known to this driver.
	Cleanup() error
}

// Driver is the interface for layered/snapshot file system drivers.
type Driver interface {
	ProtoDriver
	// Diff produces an archive of the changes between the specified
	// layer and its parent layer which may be "".
	Diff(id, parent string) (archive.Archive, error)
	// Changes produces a list of changes between the specified layer
	// and its parent layer. If parent is "", then all changes will be ADD changes.
	Changes(id, parent string) ([]archive.Change, error)
	// ApplyDiff extracts the changeset from the given diff into the
	// layer with the specified id and parent, returning the size of the
	// new layer in bytes.
	ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
	// DiffSize calculates the changes between the specified id
	// and its parent and returns the size in bytes of the changes
	// relative to its base filesystem directory.
	DiffSize(id, parent string) (size int64, err error)
}

func init() {
	drivers = make(map[string]InitFunc)
}

func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

func GetDriver(name, home string, options []string) (Driver, error) {
	if initFunc, exists := drivers[name]; exists {
		return initFunc(path.Join(home, name), options)
	}
	return nil, ErrNotSupported
}

func New(root string, options []string) (driver Driver, err error) {
	for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
		if name != "" {
			return GetDriver(name, root, options)
		}
	}

	// Check for priority drivers first
	for _, name := range priority {
		driver, err = GetDriver(name, root, options)
		if err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}

	// Check all registered drivers if no priority driver is found
	for name, initFunc := range drivers {
		if driver, err = initFunc(root, options); err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}
	return nil, fmt.Errorf("No supported storage backend found")
}

func checkPriorDriver(name, root string) {
	priorDrivers := []string{}
	for prior := range drivers {
		if prior != name && prior != "vfs" {
			if _, err := os.Stat(path.Join(root, prior)); err == nil {
				priorDrivers = append(priorDrivers, prior)
			}
		}
	}
	if len(priorDrivers) > 0 {
		logrus.Warnf("Graphdriver %s selected. Your graphdriver directory %s already contains data managed by other graphdrivers: %s", name, root, strings.Join(priorDrivers, ","))
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The Gorilla 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 pat

import (
	"net/http"

	"github.com/gorilla/mux"
)

// Creates a stack of HTTP handlers. Each HTTP handler is responsible for
// calling the next. The handlers are executed in reverse order, the last is
// called first.
func use(handler http.Handler, middleware ...func(http.Handler) http.Handler) http.Handler {
	for _, m := range middleware {
		handler = m(handler)
	}
	return handler
}

// registerPat a pattern with a handler for the given request method.
func registerPat(r *mux.Router, meth, pat string, h http.Handler, middleware []func(http.Handler) http.Handler) *mux.Route {
	return r.NewRoute().PathPrefix(pat).Handler(use(h, middleware...)).Methods(meth)
}

// Delete registers a pattern with a handler for DELETE requests.
func Delete(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "DELETE", pat, h, middleware)
}

// Get registers a pattern with a handler for GET requests.
func Get(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "GET", pat, h, middleware)
}

// Head registers a pattern with a handler for HEAD requests.
func Head(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "HEAD", pat, h, middleware)
}

// Post registers a pattern with a handler for POST requests.
func Post(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "POST", pat, h, middleware)
}

// Put registers a pattern with a handler for PUT requests.
func Put(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "PUT", pat, h, middleware)
}
<commit_msg>pat: add options<commit_after>// Copyright 2012 The Gorilla 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 pat

import (
	"net/http"

	"github.com/gorilla/mux"
)

// Creates a stack of HTTP handlers. Each HTTP handler is responsible for
// calling the next. The handlers are executed in reverse order, the last is
// called first.
func use(handler http.Handler, middleware ...func(http.Handler) http.Handler) http.Handler {
	for _, m := range middleware {
		handler = m(handler)
	}
	return handler
}

// registerPat a pattern with a handler for the given request method.
func registerPat(r *mux.Router, meth, pat string, h http.Handler, middleware []func(http.Handler) http.Handler) *mux.Route {
	return r.NewRoute().PathPrefix(pat).Handler(use(h, middleware...)).Methods(meth)
}

// Delete registers a pattern with a handler for DELETE requests.
func Delete(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "DELETE", pat, h, middleware)
}

// Get registers a pattern with a handler for GET requests.
func Get(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "GET", pat, h, middleware)
}

// Head registers a pattern with a handler for HEAD requests.
func Head(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "HEAD", pat, h, middleware)
}

// Options registers a pattern with a handler for OPTIONS requests.
func Options(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "OPTIONS", pat, h, middleware)
}

// Post registers a pattern with a handler for POST requests.
func Post(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "POST", pat, h, middleware)
}

// Put registers a pattern with a handler for PUT requests.
func Put(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "PUT", pat, h, middleware)
}
<|endoftext|>