<commit_before>package pgtype

import (
	"database/sql/driver"
	"encoding/binary"
	"fmt"
	"math"
	"strconv"

	"github.com/jackc/pgio"
)

type Int2 struct {
	Int    int16
	Status Status
}

func (dst *Int2) Set(src interface{}) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	if value, ok := src.(interface{ Get() interface{} }); ok {
		value2 := value.Get()
		if value2 != value {
			return dst.Set(value2)
		}
	}

	switch value := src.(type) {
	case int8:
		*dst = Int2{Int: int16(value), Status: Present}
	case uint8:
		*dst = Int2{Int: int16(value), Status: Present}
	case int16:
		*dst = Int2{Int: int16(value), Status: Present}
	case uint16:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case int32:
		if value < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case uint32:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case int64:
		if value < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case uint64:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case int:
		if value < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case uint:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case string:
		num, err := strconv.ParseInt(value, 10, 16)
		if err != nil {
			return err
		}
		*dst = Int2{Int: int16(num), Status: Present}
	case float32:
		if value > math.MaxInt16 {
			return fmt.Errorf("%f is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case float64:
		if value > math.MaxInt16 {
			return fmt.Errorf("%f is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case *int8:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint8:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int16:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint16:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int32:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint32:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int64:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint64:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *string:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *float32:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *float64:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	default:
		if originalSrc, ok := underlyingNumberType(src); ok {
			return dst.Set(originalSrc)
		}
		return fmt.Errorf("cannot convert %v to Int2", value)
	}

	return nil
}

func (dst Int2) Get() interface{} {
	switch dst.Status {
	case Present:
		return dst.Int
	case Null:
		return nil
	default:
		return dst.Status
	}
}

func (src *Int2) AssignTo(dst interface{}) error {
	return int64AssignTo(int64(src.Int), src.Status, dst)
}

func (dst *Int2) DecodeText(ci *ConnInfo, src []byte) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	n, err := strconv.ParseInt(string(src), 10, 16)
	if err != nil {
		return err
	}

	*dst = Int2{Int: int16(n), Status: Present}
	return nil
}

func (dst *Int2) DecodeBinary(ci *ConnInfo, src []byte) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	if len(src) != 2 {
		return fmt.Errorf("invalid length for int2: %v", len(src))
	}

	n := int16(binary.BigEndian.Uint16(src))
	*dst = Int2{Int: n, Status: Present}
	return nil
}

func (src Int2) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
	switch src.Status {
	case Null:
		return nil, nil
	case Undefined:
		return nil, errUndefined
	}

	return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil
}

func (src Int2) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
	switch src.Status {
	case Null:
		return nil, nil
	case Undefined:
		return nil, errUndefined
	}

	return pgio.AppendInt16(buf, src.Int), nil
}

// Scan implements the database/sql Scanner interface.
func (dst *Int2) Scan(src interface{}) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	switch src := src.(type) {
	case int64:
		if src < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", src)
		}
		if src > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", src)
		}
		*dst = Int2{Int: int16(src), Status: Present}
		return nil
	case string:
		return dst.DecodeText(nil, []byte(src))
	case []byte:
		srcCopy := make([]byte, len(src))
		copy(srcCopy, src)
		return dst.DecodeText(nil, srcCopy)
	}

	return fmt.Errorf("cannot scan %T", src)
}

// Value implements the database/sql/driver Valuer interface.
func (src Int2) Value() (driver.Value, error) {
	switch src.Status {
	case Present:
		return int64(src.Int), nil
	case Null:
		return nil, nil
	default:
		return nil, errUndefined
	}
}

func (src Int2) MarshalJSON() ([]byte, error) {
	switch src.Status {
	case Present:
		return []byte(strconv.FormatInt(int64(src.Int), 10)), nil
	case Null:
		return []byte("null"), nil
	case Undefined:
		return nil, errUndefined
	}

	return nil, errBadStatus
}
<commit_msg>Add UnmarshalJSON to pgtype.Int2<commit_after>package pgtype

import (
	"database/sql/driver"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"strconv"

	"github.com/jackc/pgio"
)

type Int2 struct {
	Int    int16
	Status Status
}

func (dst *Int2) Set(src interface{}) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	if value, ok := src.(interface{ Get() interface{} }); ok {
		value2 := value.Get()
		if value2 != value {
			return dst.Set(value2)
		}
	}

	switch value := src.(type) {
	case int8:
		*dst = Int2{Int: int16(value), Status: Present}
	case uint8:
		*dst = Int2{Int: int16(value), Status: Present}
	case int16:
		*dst = Int2{Int: int16(value), Status: Present}
	case uint16:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case int32:
		if value < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case uint32:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case int64:
		if value < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case uint64:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case int:
		if value < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case uint:
		if value > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case string:
		num, err := strconv.ParseInt(value, 10, 16)
		if err != nil {
			return err
		}
		*dst = Int2{Int: int16(num), Status: Present}
	case float32:
		if value > math.MaxInt16 {
			return fmt.Errorf("%f is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case float64:
		if value > math.MaxInt16 {
			return fmt.Errorf("%f is greater than maximum value for Int2", value)
		}
		*dst = Int2{Int: int16(value), Status: Present}
	case *int8:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint8:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int16:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint16:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int32:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint32:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int64:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint64:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *int:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *uint:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *string:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *float32:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	case *float64:
		if value == nil {
			*dst = Int2{Status: Null}
		} else {
			return dst.Set(*value)
		}
	default:
		if originalSrc, ok := underlyingNumberType(src); ok {
			return dst.Set(originalSrc)
		}
		return fmt.Errorf("cannot convert %v to Int2", value)
	}

	return nil
}

func (dst Int2) Get() interface{} {
	switch dst.Status {
	case Present:
		return dst.Int
	case Null:
		return nil
	default:
		return dst.Status
	}
}

func (src *Int2) AssignTo(dst interface{}) error {
	return int64AssignTo(int64(src.Int), src.Status, dst)
}

func (dst *Int2) DecodeText(ci *ConnInfo, src []byte) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	n, err := strconv.ParseInt(string(src), 10, 16)
	if err != nil {
		return err
	}

	*dst = Int2{Int: int16(n), Status: Present}
	return nil
}

func (dst *Int2) DecodeBinary(ci *ConnInfo, src []byte) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	if len(src) != 2 {
		return fmt.Errorf("invalid length for int2: %v", len(src))
	}

	n := int16(binary.BigEndian.Uint16(src))
	*dst = Int2{Int: n, Status: Present}
	return nil
}

func (src Int2) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
	switch src.Status {
	case Null:
		return nil, nil
	case Undefined:
		return nil, errUndefined
	}

	return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil
}

func (src Int2) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
	switch src.Status {
	case Null:
		return nil, nil
	case Undefined:
		return nil, errUndefined
	}

	return pgio.AppendInt16(buf, src.Int), nil
}

// Scan implements the database/sql Scanner interface.
func (dst *Int2) Scan(src interface{}) error {
	if src == nil {
		*dst = Int2{Status: Null}
		return nil
	}

	switch src := src.(type) {
	case int64:
		if src < math.MinInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", src)
		}
		if src > math.MaxInt16 {
			return fmt.Errorf("%d is greater than maximum value for Int2", src)
		}
		*dst = Int2{Int: int16(src), Status: Present}
		return nil
	case string:
		return dst.DecodeText(nil, []byte(src))
	case []byte:
		srcCopy := make([]byte, len(src))
		copy(srcCopy, src)
		return dst.DecodeText(nil, srcCopy)
	}

	return fmt.Errorf("cannot scan %T", src)
}

// Value implements the database/sql/driver Valuer interface.
func (src Int2) Value() (driver.Value, error) {
	switch src.Status {
	case Present:
		return int64(src.Int), nil
	case Null:
		return nil, nil
	default:
		return nil, errUndefined
	}
}

func (src Int2) MarshalJSON() ([]byte, error) {
	switch src.Status {
	case Present:
		return []byte(strconv.FormatInt(int64(src.Int), 10)), nil
	case Null:
		return []byte("null"), nil
	case Undefined:
		return nil, errUndefined
	}

	return nil, errBadStatus
}

func (dst *Int2) UnmarshalJSON(b []byte) error {
	var n *int16
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}

	if n == nil {
		*dst = Int2{Status: Null}
	} else {
		*dst = Int2{Int: *n, Status: Present}
	}

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAPIGatewayIntegrationResponse_basic(t *testing.T) {
	var conf apigateway.IntegrationResponse

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSAPIGatewayIntegrationResponseDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSAPIGatewayIntegrationResponseConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSAPIGatewayIntegrationResponseExists("aws_api_gateway_integration_response.test", &conf),
					testAccCheckAWSAPIGatewayIntegrationResponseAttributes(&conf),
					resource.TestCheckResourceAttr(
						"aws_api_gateway_integration_response.test", "response_templates.application/json", ""),
					resource.TestCheckResourceAttr(
						"aws_api_gateway_integration_response.test", "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
				),
			},
		},
	})
}

func testAccCheckAWSAPIGatewayIntegrationResponseAttributes(conf *apigateway.IntegrationResponse) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if *conf.StatusCode != "400" {
			return fmt.Errorf("wrong StatusCode: %q", *conf.StatusCode)
		}
		if conf.ResponseTemplates["application/json"] != nil {
			return fmt.Errorf("wrong ResponseTemplate for application/json")
		}
		if *conf.ResponseTemplates["application/xml"] != "#set($inputRoot = $input.path('$'))\n{ }" {
			return fmt.Errorf("wrong ResponseTemplate for application/xml")
		}
		return nil
	}
}

func testAccCheckAWSAPIGatewayIntegrationResponseExists(n string, res *apigateway.IntegrationResponse) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No API Gateway Method ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).apigateway

		req := &apigateway.GetIntegrationResponseInput{
			HttpMethod: aws.String("GET"),
			ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID),
			RestApiId:  aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID),
			StatusCode: aws.String(rs.Primary.Attributes["status_code"]),
		}
		describe, err := conn.GetIntegrationResponse(req)
		if err != nil {
			return err
		}

		*res = *describe

		return nil
	}
}

func testAccCheckAWSAPIGatewayIntegrationResponseDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).apigateway

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_api_gateway_integration_response" {
			continue
		}

		req := &apigateway.GetIntegrationResponseInput{
			HttpMethod: aws.String("GET"),
			ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID),
			RestApiId:  aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID),
			StatusCode: aws.String(rs.Primary.Attributes["status_code"]),
		}
		_, err := conn.GetIntegrationResponse(req)

		if err == nil {
			return fmt.Errorf("API Gateway Method still exists")
		}

		aws2err, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if aws2err.Code() != "NotFoundException" {
			return err
		}

		return nil
	}

	return nil
}

const testAccAWSAPIGatewayIntegrationResponseConfig = `
resource "aws_api_gateway_rest_api" "test" {
  name = "test"
}

resource "aws_api_gateway_resource" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
  path_part = "test"
}

resource "aws_api_gateway_method" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "GET"
  authorization = "NONE"

  request_models = {
    "application/json" = "Error"
  }
}

resource "aws_api_gateway_method_response" "error" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "${aws_api_gateway_method.test.http_method}"
  status_code = "400"

  response_models = {
    "application/json" = "Error"
  }
}

resource "aws_api_gateway_integration" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "${aws_api_gateway_method.test.http_method}"

  request_templates = {
    "application/json" = ""
    "application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
  }

  type = "MOCK"
}

resource "aws_api_gateway_integration_response" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "${aws_api_gateway_method.test.http_method}"
  status_code = "${aws_api_gateway_method_response.error.status_code}"

  response_templates = {
    "application/json" = ""
    "application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
  }
}
`
<commit_msg>provider/aws: Add regression test for #5891<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAPIGatewayIntegrationResponse_basic(t *testing.T) {
	var conf apigateway.IntegrationResponse

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSAPIGatewayIntegrationResponseDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSAPIGatewayIntegrationResponseConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSAPIGatewayIntegrationResponseExists("aws_api_gateway_integration_response.test", &conf),
					testAccCheckAWSAPIGatewayIntegrationResponseAttributes(&conf),
					resource.TestCheckResourceAttr(
						"aws_api_gateway_integration_response.test", "response_templates.application/json", ""),
					resource.TestCheckResourceAttr(
						"aws_api_gateway_integration_response.test", "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
				),
			},
		},
	})
}

func testAccCheckAWSAPIGatewayIntegrationResponseAttributes(conf *apigateway.IntegrationResponse) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if *conf.StatusCode != "400" {
			return fmt.Errorf("wrong StatusCode: %q", *conf.StatusCode)
		}
		if conf.ResponseTemplates["application/json"] != nil {
			return fmt.Errorf("wrong ResponseTemplate for application/json")
		}
		if *conf.ResponseTemplates["application/xml"] != "#set($inputRoot = $input.path('$'))\n{ }" {
			return fmt.Errorf("wrong ResponseTemplate for application/xml")
		}
		if conf.SelectionPattern == nil || *conf.SelectionPattern != ".*" {
			return fmt.Errorf("wrong SelectionPattern (expected .*)")
		}
		return nil
	}
}

func testAccCheckAWSAPIGatewayIntegrationResponseExists(n string, res *apigateway.IntegrationResponse) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No API Gateway Method ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).apigateway

		req := &apigateway.GetIntegrationResponseInput{
			HttpMethod: aws.String("GET"),
			ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID),
			RestApiId:  aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID),
			StatusCode: aws.String(rs.Primary.Attributes["status_code"]),
		}
		describe, err := conn.GetIntegrationResponse(req)
		if err != nil {
			return err
		}

		*res = *describe

		return nil
	}
}

func testAccCheckAWSAPIGatewayIntegrationResponseDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).apigateway

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_api_gateway_integration_response" {
			continue
		}

		req := &apigateway.GetIntegrationResponseInput{
			HttpMethod: aws.String("GET"),
			ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID),
			RestApiId:  aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID),
			StatusCode: aws.String(rs.Primary.Attributes["status_code"]),
		}
		_, err := conn.GetIntegrationResponse(req)

		if err == nil {
			return fmt.Errorf("API Gateway Method still exists")
		}

		aws2err, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if aws2err.Code() != "NotFoundException" {
			return err
		}

		return nil
	}

	return nil
}

const testAccAWSAPIGatewayIntegrationResponseConfig = `
resource "aws_api_gateway_rest_api" "test" {
  name = "test"
}

resource "aws_api_gateway_resource" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
  path_part = "test"
}

resource "aws_api_gateway_method" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "GET"
  authorization = "NONE"

  request_models = {
    "application/json" = "Error"
  }
}

resource "aws_api_gateway_method_response" "error" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "${aws_api_gateway_method.test.http_method}"
  status_code = "400"

  response_models = {
    "application/json" = "Error"
  }
}

resource "aws_api_gateway_integration" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "${aws_api_gateway_method.test.http_method}"

  request_templates = {
    "application/json" = ""
    "application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
  }

  type = "MOCK"
}

resource "aws_api_gateway_integration_response" "test" {
  rest_api_id = "${aws_api_gateway_rest_api.test.id}"
  resource_id = "${aws_api_gateway_resource.test.id}"
  http_method = "${aws_api_gateway_method.test.http_method}"
  status_code = "${aws_api_gateway_method_response.error.status_code}"
  selection_pattern = ".*"

  response_templates = {
    "application/json" = ""
    "application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
  }
}
`
<|endoftext|>


<commit_before>// Copyright 2020 Sven Rebhan.
// Licensed under the MIT license which can be found in the LICENSE file.

package dpt

import (
	"reflect"
	"sync"
)

var (
	types = [...]DatapointValue{
		// 1.xxx
		new(DPT_1001),
		new(DPT_1002),
		new(DPT_1003),
		new(DPT_1009),
		new(DPT_1010),

		// 5.xxx
		new(DPT_5001),
		new(DPT_5003),
		new(DPT_5004),

		// 9.xxx
		new(DPT_9001),
		new(DPT_9004),
		new(DPT_9005),
		new(DPT_9007),

		// 12.xxx
		new(DPT_12001),

		// 13.xxx
		new(DPT_13001),
		new(DPT_13002),
		new(DPT_13010),
		new(DPT_13011),
		new(DPT_13012),
		new(DPT_13013),
		new(DPT_13014),
		new(DPT_13015),
	}
	once     sync.Once
	registry map[string]reflect.Type
)

// Init function used to add all types
func setup() {
	// Register the types
	registry = make(map[string]reflect.Type)
	for _, d := range types {
		// Determine the name of the datatype
		d_type := reflect.TypeOf(d).Elem()
		name := d_type.Name()

		// Convert the name into KNX yy.xxx (e.g. DPT_1001 --> 1.001) format
		name = name[4:len(name)-3] + "." + name[len(name)-3:]

		// Register the type
		registry[name] = d_type
	}
}

func ListSupportedTypes() []string {
	// Singleton, can only run once
	once.Do(setup)

	// Initialize the key-list
	keys := make([]string, len(registry))

	// Fill the key-list
	i := 0
	for k := range registry {
		keys[i] = k
		i++
	}

	return keys
}

func Produce(name string) (d DatapointValue, ok bool) {
	// Singleton, can only run once
	once.Do(setup)

	x, ok := registry[name]

	if ok {
		d = reflect.New(x).Interface().(DatapointValue)
	}
	return
}
<commit_msg>Protect setup function in its body rather than protecting the callers.<commit_after>// Copyright 2020 Sven Rebhan.
// Licensed under the MIT license which can be found in the LICENSE file.

package dpt

import (
	"reflect"
	"sync"
)

var (
	types = [...]DatapointValue{
		// 1.xxx
		new(DPT_1001),
		new(DPT_1002),
		new(DPT_1003),
		new(DPT_1009),
		new(DPT_1010),

		// 5.xxx
		new(DPT_5001),
		new(DPT_5003),
		new(DPT_5004),

		// 9.xxx
		new(DPT_9001),
		new(DPT_9004),
		new(DPT_9005),
		new(DPT_9007),

		// 12.xxx
		new(DPT_12001),

		// 13.xxx
		new(DPT_13001),
		new(DPT_13002),
		new(DPT_13010),
		new(DPT_13011),
		new(DPT_13012),
		new(DPT_13013),
		new(DPT_13014),
		new(DPT_13015),
	}
	once     sync.Once
	registry map[string]reflect.Type
)

// Init function used to add all types
func setup() {
	// Singleton, can only run once
	once.Do(func() {
		// Register the types
		registry = make(map[string]reflect.Type)
		for _, d := range types {
			// Determine the name of the datatype
			d_type := reflect.TypeOf(d).Elem()
			name := d_type.Name()

			// Convert the name into KNX yy.xxx (e.g. DPT_1001 --> 1.001) format
			name = name[4:len(name)-3] + "." + name[len(name)-3:]

			// Register the type
			registry[name] = d_type
		}
	})
}

func ListSupportedTypes() []string {
	// Setup the registry
	setup()

	// Initialize the key-list
	keys := make([]string, len(registry))

	// Fill the key-list
	i := 0
	for k := range registry {
		keys[i] = k
		i++
	}

	return keys
}

func Produce(name string) (d DatapointValue, ok bool) {
	// Setup the registry
	setup()

	// Lookup the given type and create a new instance of that type
	x, ok := registry[name]
	if ok {
		d = reflect.New(x).Interface().(DatapointValue)
	}
	return d, ok
}
<|endoftext|>


<commit_before>// Matrix functions necessary for eigendecomposition of electron Hamiltonian
// Inspired by matrix implementation in go-gsl
// (https://bitbucket.org/fhs/go-gsl)
package percolation

// some of these included packages may not be necessary

/*
#cgo LDFLAGS: -lgsl -lgslcblas
#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
*/
import "C"

type SymmetricMatrix struct {
	// dimensions (symmetric, so lx = ly)
	length int
	// Data is a map since many rows/cols may be empty.
	// first index: row, second index: column
	data map[int]map[int]float64
}

// Return a new SymmetricMatrix without the empty rows (and columns) in sym.
// The map returned converts from row indices in the returned matrix to row
// indices in the original matrix.
func (sym *SymmetricMatrix) RemoveEmptyRows() (*SymmetricMatrix, map[int]int) {
	return nil, nil
}

// Return an ordered slice of the eigenvalues of sym, and a slice of the
// eigenvectors in the same order.
func (sym *SymmetricMatrix) Eigensystem() ([]float64, [][]float64) {
	return nil, nil
}

// Return the GSL matrix representation of sym.
func (sym *SymmetricMatrix) toMatrix() *C.gsl_matrix {
	// start with a zeroed matrix (m)

	// iterate over rows in sym (row = i)

	// for each column j in the row i: val = sym(i, j)
	// if diagonal: m(i,j) = val
	// if not diagonal: m(i,j) = val and m(j, i) = val
	return nil
}

func vectorToSlice(v *C.gsl_vector) []float64 {
	xs := []float64{}
	var i C.size_t
	for i = 0; i < v.size; i++ {
		xs = append(xs, float64(C.gsl_vector_get(v, i)))
	}
	return xs
}

func matrixToSlices(m *C.gsl_matrix) [][]float64 {
	vectors := [][]float64{}
	var i, j C.size_t
	for i = 0; i < m.size1; i++ {
		xs := []float64{}
		for j = 0; j < m.size2; j++ {
			xs = append(xs, float64(C.gsl_matrix_get(m, i, j)))
		}
		vectors = append(vectors, xs)
	}
	return vectors
}
<commit_msg>symmetric matrix creation and access<commit_after>// Matrix functions necessary for eigendecomposition of electron Hamiltonian
// Inspired by matrix implementation in go-gsl
// (https://bitbucket.org/fhs/go-gsl)
package percolation

// some of these included packages may not be necessary

/*
#cgo LDFLAGS: -lgsl -lgslcblas
#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
*/
import "C"

type SymmetricMatrix struct {
	// dimensions (symmetric, so lx = ly)
	length int
	// Data is a map since many rows/cols may be empty.
	// first index: row, second index: column
	data map[int]map[int]float64
}

// Return a zeroed LxL symmetric matric.
func NewSymmetricMatrix(L int) *SymmetricMatrix {
	data := make(map[int]map[int]float64)
	sym := new(SymmetricMatrix)
	sym.length = L
	sym.data = data
	return sym
}

// Return the value at row i, column j in sym.
func (sym *SymmetricMatrix) Get(i, j int) float64 {
	if i > sym.length || j > sym.length {
		panic("matrix access out of bounds")
	}
	row, ok := sym.data[i]
	if !ok {
		return 0
	}
	val, ok := row[j]
	if !ok {
		return 0
	}
	return val
}

// Set the value at row i, column j in sym to val.
func (sym *SymmetricMatrix) Set(i, j int, val float64) {
	if i > sym.length || j > sym.length {
		panic("matrix access out of bounds")
	}
	row, ok := sym.data[i]
	if !ok {
		// row doesn't exist yet, need to create it
		row = make(map[int]float64)
		sym.data[i] = row
	}
	row[j] = val
}

// Return a new SymmetricMatrix without the empty rows (and columns) in sym.
// The map returned converts from row indices in the returned matrix to row
// indices in the original matrix.
func (sym *SymmetricMatrix) RemoveEmptyRows() (*SymmetricMatrix, map[int]int) {
	return nil, nil
}

// Return an ordered slice of the eigenvalues of sym, and a slice of the
// eigenvectors in the same order.
func (sym *SymmetricMatrix) Eigensystem() ([]float64, [][]float64) {
	return nil, nil
}

// Return the GSL matrix representation of sym.
func (sym *SymmetricMatrix) toMatrix() *C.gsl_matrix {
	// start with a zeroed matrix (m)

	// iterate over rows in sym (row = i)

	// for each column j in the row i: val = sym(i, j)
	// if diagonal: m(i,j) = val
	// if not diagonal: m(i,j) = val and m(j, i) = val
	return nil
}

func vectorToSlice(v *C.gsl_vector) []float64 {
	xs := []float64{}
	var i C.size_t
	for i = 0; i < v.size; i++ {
		xs = append(xs, float64(C.gsl_vector_get(v, i)))
	}
	return xs
}

func matrixToSlices(m *C.gsl_matrix) [][]float64 {
	vectors := [][]float64{}
	var i, j C.size_t
	for i = 0; i < m.size1; i++ {
		xs := []float64{}
		for j = 0; j < m.size2; j++ {
			xs = append(xs, float64(C.gsl_matrix_get(m, i, j)))
		}
		vectors = append(vectors, xs)
	}
	return vectors
}
<|endoftext|>


<commit_before>package apollostats

import (
	"html"
	"net/url"
	"sort"
	"strings"
	"time"
)

type Ban struct {
	ID         int64     `gorm:"column:id;primary_key"`
	Timestamp  time.Time `gorm:"column:bantime"`
	CKey       string    `gorm:"column:ckey"`
	CID        string    `gorm:"column:computerid"`
	IP         string    `gorm:"column:ip"`
	BanType    string    `gorm:"column:bantype"`
	BannedJob  string    `gorm:"column:job"`
	Admin      string    `gorm:"column:a_ckey"`
	Reason     string    `gorm:"column:reason"`
	Duration   int64     `gorm:"column:duration"`
	Expiration time.Time `gorm:"column:expiration_time"`
}

func (b *Ban) TableName() string {
	return "ban"
}

// Return a nicely formatted ban type.
func (b *Ban) Ban() string {
	if b.BanType == "PERMABAN" || b.BanType == "TEMPBAN" {
		return "Server"
	} else if b.BanType == "JOB_PERMABAN" || b.BanType == "JOB_TEMPBAN" {
		return strings.Title(b.BannedJob)
	}
	return strings.Title(b.BanType)
}

// Return a nicely formatted expire time, as a string.
func (b *Ban) Expires() string {
	if b.Duration < 0 {
		return "Permanent"
	} else {
		return b.Expiration.Format("2006-01-02 15:04 MST")
	}
}

// Clean up the ban reason and return a nicely formatted string instead.
func (b *Ban) Message() string {
	m := strings.TrimPrefix(b.Reason, "(MANUAL BAN) ")
	// Make sure the message ends with a dot, but avoid making double dots.
	m = html.UnescapeString(m)
	m = strings.TrimSuffix(m, ".") + "."
	return m
}

type AccountItem struct {
	ID        int64     `gorm:"column:id;primary_key"`
	Timestamp time.Time `gorm:"column:time"`
	CKey      string    `gorm:"column:ckey"`
	Item      string    `gorm:"column:item"`
}

func (a *AccountItem) TableName() string {
	return "acc_items"
}

type Death struct {
	ID        int64     `gorm:"column:id;primary_key"`
	RoundID   int64     `gorm:"column:round_id"`
	Timestamp time.Time `gorm:"column:tod"`
	Name      string    `gorm:"column:name"`
	Job       string    `gorm:"column:job"`
	Room      string    `gorm:"column:pod"`
	Position  string    `gorm:"column:coord"`
	Brute     int64     `gorm:"column:bruteloss"`
	Brain     int64     `gorm:"column:brainloss"`
	Fire      int64     `gorm:"column:fireloss"`
	Oxygen    int64     `gorm:"column:oxyloss"`
}

func (d *Death) TableName() string {
	return "deaths"
}

// Return a cleaned up room name.
func (d *Death) RoomName() string {
	// TODO: fix this thing in the byond source.
	// Cleanup the room name
	return strings.Trim(d.Room, "ÿ")
}

type RoundAntags struct {
	ID      int64  `gorm:"column:id"`
	RoundID int64  `gorm:"column:round_id"`
	Name    string `gorm:"column:name"`
	Job     string `gorm:"column:job"`
	Role    string `gorm:"column:role"`
	Success bool   `gorm:"column:success"`
}

func (r *RoundAntags) TableName() string {
	return "round_antags"
}

type RoundAILaws struct {
	ID      int64  `gorm:"column:id"`
	RoundID int64  `gorm:"column:round_id"`
	Law     string `gorm:"column:law"`
}

func (r *RoundAILaws) TableName() string {
	return "round_ai_laws"
}

type RoundStats struct {
	ID       int64     `gorm:"column:id"`
	GameMode string    `gorm:"column:game_mode"`
	EndTime  time.Time `gorm:"column:end_time"`
	Duration int64     `gorm:"column:duration"`

	Productivity    int64 `gorm:"column:productivity"`
	Deaths          int64 `gorm:"column:deaths"`
	Clones          int64 `gorm:"column:clones"`
	DispenseVolume  int64 `gorm:"column:dispense_volume"`
	BombsExploded   int64 `gorm:"column:bombs_exploded"`
	Vended          int64 `gorm:"column:vended"`
	RunDistance     int64 `gorm:"column:run_distance"`
	BloodMopped     int64 `gorm:"column:blood_mopped"`
	DamageCost      int64 `gorm:"column:damage_cost"`
	BreakTime       int64 `gorm:"column:break_time"`
	MonkeyDeaths    int64 `gorm:"column:monkey_deaths"`
	SpamBlocked     int64 `gorm:"column:spam_blocked"`
	PeopleSlipped   int64 `gorm:"column:people_slipped"`
	DoorsOpened     int64 `gorm:"column:doors_opened"`
	GunsFired       int64 `gorm:"column:guns_fired"`
	BeepskyBeatings int64 `gorm:"column:beepsky_beatings"`
	DoorsWelded     int64 `gorm:"column:doors_welded"`
	Totalkwh        int64 `gorm:"column:total_kwh"`
	Artifacts       int64 `gorm:"column:artifacts"`
	CargoProfit     int64 `gorm:"column:cargo_profit"`
	TrashVented     int64 `gorm:"column:trash_vented"`
	AIFollow        int64 `gorm:"column:ai_follow"`
	Banned          int64 `gorm:"column:banned"`
}

func (r *RoundStats) TableName() string {
	return "round_stats"
}

type Character struct {
	ID                 int64  `gorm:"column:id"`
	CKey               string `gorm:"column:ckey"`
	Name               string `gorm:"column:name"`
	Gender             string `gorm:"column:gender"`
	BirthDate          string `gorm:"column:birth_date"`
	Species            string `gorm:"column:species"`
	HomeSystem         string `gorm:"column:home_system"`
	Citizenship        string `gorm:"column:citizenship"`
	Faction            string `gorm:"column:faction"`
	Religion           string `gorm:"column:religion"`
	NanotrasenRelation string `gorm:"column:nanotrasen_relation"`
	Department         int64  `gorm:"column:department"`
	Roles              string `gorm:"column:roles"`
	FlavorTextsHuman   string `gorm:"column:flavor_texts_human"`
	FlavorTextsRobot   string `gorm:"column:flavor_texts_robot"`
	EmpNotes           string `gorm:"column:gen_notes"`
	MedNotes           string `gorm:"column:med_notes"`
	SecNotes           string `gorm:"column:sec_notes"`
	EmpRecords         string `gorm:"column:gen_record"`
	MedRecords         string `gorm:"column:med_record"`
	SecRecords         string `gorm:"column:sec_record"`
	JobAntag           int64  `gorm:"column:job_antag"`
	ExploitRecords     string `gorm:"column:exploit_record"`
}

func (c *Character) TableName() string {
	return "characters"
}

func (c *Character) NiceName() string {
	if len(strings.TrimSpace(c.Name)) == 0 {
		return "<NO NAME>"
	}
	return strings.Title(c.Name)
}

func (c *Character) NiceGender() string {
	switch c.Species {
	case "Machine", "Diona", "Wryn":
		return "None"
	case "Tajara", "Unathi", "Skrell", "Nucleation", "Human":
		if len(strings.TrimSpace(c.Gender)) > 1 {
			return strings.Title(c.Gender)
		}
	}
	return "Unknown"
}

func (c *Character) NiceBirthDate() string {
	t, e := time.Parse("2006&1&2", strings.Replace(c.BirthDate, "&", "&", -1))
	if e != nil {
		return "Can't parse date"
	}

	return t.Format("2006-01-02")
}

func (c *Character) NiceDep() string {
	switch c.Department {
	case 0:
		return "Civilian"
	case 1:
		return "Engineering"
	case 2:
		return "Supply"
	case 3:
		return "Medical"
	case 4:
		return "Science"
	case 5:
		return "Security"
	case 6:
		return "Synthetic"
	}
	return "Unknown"
}

func (c *Character) Flavor() string {
	if c.Species == "Machine" {
		return c.FlavorTextsRobot
	}
	return c.FlavorTextsHuman
}

func (c *Character) UnlockedJobs() []string {
	var jobs []string
	tmp, e := url.QueryUnescape(strings.Replace(c.Roles, "&", "&", -1))
	if e != nil {
		return []string{"Error parsing jobs"}
	}
	for _, s := range strings.Split(tmp, "&") {
		jobs = append(jobs, strings.Split(s, "=")[0])
	}
	sort.Strings(jobs)
	return jobs
}

func (c *Character) Records() map[string]string {
	records := map[string]string{
		"Employee": html.UnescapeString(c.EmpRecords),
		"Medical":  html.UnescapeString(c.MedRecords),
		"Security": html.UnescapeString(c.SecRecords),
	}
	return records
}
<commit_msg>Wouldn't suprise me if there's more fucking html entities in here.<commit_after>package apollostats

import (
	"html"
	"net/url"
	"sort"
	"strings"
	"time"
)

type Ban struct {
	ID         int64     `gorm:"column:id;primary_key"`
	Timestamp  time.Time `gorm:"column:bantime"`
	CKey       string    `gorm:"column:ckey"`
	CID        string    `gorm:"column:computerid"`
	IP         string    `gorm:"column:ip"`
	BanType    string    `gorm:"column:bantype"`
	BannedJob  string    `gorm:"column:job"`
	Admin      string    `gorm:"column:a_ckey"`
	Reason     string    `gorm:"column:reason"`
	Duration   int64     `gorm:"column:duration"`
	Expiration time.Time `gorm:"column:expiration_time"`
}

func (b *Ban) TableName() string {
	return "ban"
}

// Return a nicely formatted ban type.
func (b *Ban) Ban() string {
	if b.BanType == "PERMABAN" || b.BanType == "TEMPBAN" {
		return "Server"
	} else if b.BanType == "JOB_PERMABAN" || b.BanType == "JOB_TEMPBAN" {
		return strings.Title(b.BannedJob)
	}
	return strings.Title(b.BanType)
}

// Return a nicely formatted expire time, as a string.
func (b *Ban) Expires() string {
	if b.Duration < 0 {
		return "Permanent"
	} else {
		return b.Expiration.Format("2006-01-02 15:04 MST")
	}
}

// Clean up the ban reason and return a nicely formatted string instead.
func (b *Ban) Message() string {
	m := strings.TrimPrefix(b.Reason, "(MANUAL BAN) ")
	// Make sure the message ends with a dot, but avoid making double dots.
	m = html.UnescapeString(m)
	m = strings.TrimSuffix(m, ".") + "."
	return m
}

type AccountItem struct {
	ID        int64     `gorm:"column:id;primary_key"`
	Timestamp time.Time `gorm:"column:time"`
	CKey      string    `gorm:"column:ckey"`
	Item      string    `gorm:"column:item"`
}

func (a *AccountItem) TableName() string {
	return "acc_items"
}

type Death struct {
	ID        int64     `gorm:"column:id;primary_key"`
	RoundID   int64     `gorm:"column:round_id"`
	Timestamp time.Time `gorm:"column:tod"`
	Name      string    `gorm:"column:name"`
	Job       string    `gorm:"column:job"`
	Room      string    `gorm:"column:pod"`
	Position  string    `gorm:"column:coord"`
	Brute     int64     `gorm:"column:bruteloss"`
	Brain     int64     `gorm:"column:brainloss"`
	Fire      int64     `gorm:"column:fireloss"`
	Oxygen    int64     `gorm:"column:oxyloss"`
}

func (d *Death) TableName() string {
	return "deaths"
}

// Return a cleaned up room name.
func (d *Death) RoomName() string {
	// TODO: fix this thing in the byond source.
	// Cleanup the room name
	return strings.Trim(d.Room, "ÿ")
}

type RoundAntags struct {
	ID      int64  `gorm:"column:id"`
	RoundID int64  `gorm:"column:round_id"`
	Name    string `gorm:"column:name"`
	Job     string `gorm:"column:job"`
	Role    string `gorm:"column:role"`
	Success bool   `gorm:"column:success"`
}

func (r *RoundAntags) TableName() string {
	return "round_antags"
}

type RoundAILaws struct {
	ID      int64  `gorm:"column:id"`
	RoundID int64  `gorm:"column:round_id"`
	Law     string `gorm:"column:law"`
}

func (r *RoundAILaws) TableName() string {
	return "round_ai_laws"
}

type RoundStats struct {
	ID       int64     `gorm:"column:id"`
	GameMode string    `gorm:"column:game_mode"`
	EndTime  time.Time `gorm:"column:end_time"`
	Duration int64     `gorm:"column:duration"`

	Productivity    int64 `gorm:"column:productivity"`
	Deaths          int64 `gorm:"column:deaths"`
	Clones          int64 `gorm:"column:clones"`
	DispenseVolume  int64 `gorm:"column:dispense_volume"`
	BombsExploded   int64 `gorm:"column:bombs_exploded"`
	Vended          int64 `gorm:"column:vended"`
	RunDistance     int64 `gorm:"column:run_distance"`
	BloodMopped     int64 `gorm:"column:blood_mopped"`
	DamageCost      int64 `gorm:"column:damage_cost"`
	BreakTime       int64 `gorm:"column:break_time"`
	MonkeyDeaths    int64 `gorm:"column:monkey_deaths"`
	SpamBlocked     int64 `gorm:"column:spam_blocked"`
	PeopleSlipped   int64 `gorm:"column:people_slipped"`
	DoorsOpened     int64 `gorm:"column:doors_opened"`
	GunsFired       int64 `gorm:"column:guns_fired"`
	BeepskyBeatings int64 `gorm:"column:beepsky_beatings"`
	DoorsWelded     int64 `gorm:"column:doors_welded"`
	Totalkwh        int64 `gorm:"column:total_kwh"`
	Artifacts       int64 `gorm:"column:artifacts"`
	CargoProfit     int64 `gorm:"column:cargo_profit"`
	TrashVented     int64 `gorm:"column:trash_vented"`
	AIFollow        int64 `gorm:"column:ai_follow"`
	Banned          int64 `gorm:"column:banned"`
}

func (r *RoundStats) TableName() string {
	return "round_stats"
}

type Character struct {
	ID                 int64  `gorm:"column:id"`
	CKey               string `gorm:"column:ckey"`
	Name               string `gorm:"column:name"`
	Gender             string `gorm:"column:gender"`
	BirthDate          string `gorm:"column:birth_date"`
	Species            string `gorm:"column:species"`
	HomeSystem         string `gorm:"column:home_system"`
	Citizenship        string `gorm:"column:citizenship"`
	Faction            string `gorm:"column:faction"`
	Religion           string `gorm:"column:religion"`
	NanotrasenRelation string `gorm:"column:nanotrasen_relation"`
	Department         int64  `gorm:"column:department"`
	Roles              string `gorm:"column:roles"`
	FlavorTextsHuman   string `gorm:"column:flavor_texts_human"`
	FlavorTextsRobot   string `gorm:"column:flavor_texts_robot"`
	EmpNotes           string `gorm:"column:gen_notes"`
	MedNotes           string `gorm:"column:med_notes"`
	SecNotes           string `gorm:"column:sec_notes"`
	EmpRecords         string `gorm:"column:gen_record"`
	MedRecords         string `gorm:"column:med_record"`
	SecRecords         string `gorm:"column:sec_record"`
	JobAntag           int64  `gorm:"column:job_antag"`
	ExploitRecords     string `gorm:"column:exploit_record"`
}

func (c *Character) TableName() string {
	return "characters"
}

func (c *Character) NiceName() string {
	if len(strings.TrimSpace(c.Name)) == 0 {
		return "<NO NAME>"
	}
	return strings.Title(c.Name)
}

func (c *Character) NiceGender() string {
	switch c.Species {
	case "Machine", "Diona", "Wryn":
		return "None"
	case "Tajara", "Unathi", "Skrell", "Nucleation", "Human":
		if len(strings.TrimSpace(c.Gender)) > 1 {
			return strings.Title(c.Gender)
		}
	}
	return "Unknown"
}

func (c *Character) NiceBirthDate() string {
	t, e := time.Parse("2006&1&2", strings.Replace(c.BirthDate, "&", "&", -1))
	if e != nil {
		return "Can't parse date"
	}

	return t.Format("2006-01-02")
}

func (c *Character) NiceDep() string {
	switch c.Department {
	case 0:
		return "Civilian"
	case 1:
		return "Engineering"
	case 2:
		return "Supply"
	case 3:
		return "Medical"
	case 4:
		return "Science"
	case 5:
		return "Security"
	case 6:
		return "Synthetic"
	}
	return "Unknown"
}

func (c *Character) Flavor() string {
	if c.Species == "Machine" {
		return html.UnescapeString(c.FlavorTextsRobot)
	}
	return html.UnescapeString(c.FlavorTextsHuman)
}

func (c *Character) UnlockedJobs() []string {
	var jobs []string
	tmp, e := url.QueryUnescape(strings.Replace(c.Roles, "&", "&", -1))
	if e != nil {
		return []string{"Error parsing jobs"}
	}
	for _, s := range strings.Split(tmp, "&") {
		jobs = append(jobs, strings.Split(s, "=")[0])
	}
	sort.Strings(jobs)
	return jobs
}

func (c *Character) Records() map[string]string {
	records := map[string]string{
		"Employee": html.UnescapeString(c.EmpRecords),
		"Medical":  html.UnescapeString(c.MedRecords),
		"Security": html.UnescapeString(c.SecRecords),
	}
	return records
}
<|endoftext|>


<commit_before>package service

import (
	"context"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"

	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/tag"

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

func CommonMetricsView(ctx context.Context) []*view.View {
	allocStats := stats.Int64(
		"cds/alloc",
		"Alloc is bytes of allocated heap objects",
		stats.UnitBytes,
	)

	totalAllocStats := stats.Int64(
		fmt.Sprintf("cds/total_alloc"),
		"Total Alloc is cumulative bytes allocated for heap objects",
		stats.UnitBytes,
	)

	sysStats := stats.Int64(
		fmt.Sprintf("cds/sys"),
		"Sys is the total bytes of memory obtained from the OS",
		stats.UnitBytes,
	)

	gcStats := stats.Int64(
		fmt.Sprintf("cds/num_gc"),
		"NumGC is the number of completed GC cycles",
		stats.UnitDimensionless,
	)

	tagHostname, _ := tag.NewKey("hostname")

	allocView := view.View{
		Name:        "cds/mem/alloc",
		Description: allocStats.Description(),
		Measure:     allocStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	totalAllocView := view.View{
		Name:        "cds/mem/total_alloc",
		Description: totalAllocStats.Description(),
		Measure:     totalAllocStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	sysView := view.View{
		Name:        "cds/mem/sys",
		Description: sysStats.Description(),
		Measure:     sysStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	gcView := view.View{
		Name:        "cds/mem/gc",
		Description: gcStats.Description(),
		Measure:     gcStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	sdk.GoRoutine(ctx, "service_metrics", func(ctx context.Context) {
		hostname, _ := os.Hostname()
		ctx, _ = tag.New(ctx, tag.Upsert(tagHostname, hostname))

		var maxMemoryS = os.Getenv("CDS_MAX_HEAP_SIZE")
		var maxMemory uint64
		var onceMaxMemorySignal = new(sync.Once)
		if maxMemoryS != "" {
			maxMemory, _ = strconv.ParseUint(maxMemoryS, 10, 64)
		}

		var tick = time.NewTicker(10 * time.Second)
		defer tick.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case <-tick.C:
				var m runtime.MemStats
				runtime.ReadMemStats(&m)
				stats.Record(ctx, allocStats.M(int64(m.Alloc)))
				stats.Record(ctx, totalAllocStats.M(int64(m.TotalAlloc)))
				stats.Record(ctx, sysStats.M(int64(m.Sys)))
				stats.Record(ctx, gcStats.M(int64(m.NumGC)))

				if maxMemory > 0 && m.Alloc >= maxMemory {
					onceMaxMemorySignal.Do(func() {
						p, err := os.FindProcess(os.Getpid())
						if err != nil {
							log.Error("unable to find current process: %v", err)
							return
						}
						if err := p.Signal(sdk.SIGINFO); err != nil {
							log.Error("unable to send signal: %v", err)
							return
						}
						log.Info("metrics> SIGINFO signal send to %v", os.Getpid())
					})
				}
			}
		}
	})

	return []*view.View{&allocView, &totalAllocView, &sysView, &gcView}
}
<commit_msg>Update engine/service/metrics.go<commit_after>package service

import (
	"context"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"

	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/tag"

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

func CommonMetricsView(ctx context.Context) []*view.View {
	allocStats := stats.Int64(
		"cds/alloc",
		"Alloc is bytes of allocated heap objects",
		stats.UnitBytes,
	)

	totalAllocStats := stats.Int64(
		fmt.Sprintf("cds/total_alloc"),
		"Total Alloc is cumulative bytes allocated for heap objects",
		stats.UnitBytes,
	)

	sysStats := stats.Int64(
		fmt.Sprintf("cds/sys"),
		"Sys is the total bytes of memory obtained from the OS",
		stats.UnitBytes,
	)

	gcStats := stats.Int64(
		fmt.Sprintf("cds/num_gc"),
		"NumGC is the number of completed GC cycles",
		stats.UnitDimensionless,
	)

	tagHostname, _ := tag.NewKey("hostname")

	allocView := view.View{
		Name:        "cds/mem/alloc",
		Description: allocStats.Description(),
		Measure:     allocStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	totalAllocView := view.View{
		Name:        "cds/mem/total_alloc",
		Description: totalAllocStats.Description(),
		Measure:     totalAllocStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	sysView := view.View{
		Name:        "cds/mem/sys",
		Description: sysStats.Description(),
		Measure:     sysStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	gcView := view.View{
		Name:        "cds/mem/gc",
		Description: gcStats.Description(),
		Measure:     gcStats,
		TagKeys:     []tag.Key{tagHostname},
		Aggregation: view.LastValue(),
	}

	sdk.GoRoutine(ctx, "service_metrics", func(ctx context.Context) {
		hostname, _ := os.Hostname()
		ctx, _ = tag.New(ctx, tag.Upsert(tagHostname, hostname))

		var maxMemoryS = os.Getenv("CDS_MAX_HEAP_SIZE") // in bytes
		var maxMemory uint64
		var onceMaxMemorySignal = new(sync.Once)
		if maxMemoryS != "" {
			maxMemory, _ = strconv.ParseUint(maxMemoryS, 10, 64)
		}

		var tick = time.NewTicker(10 * time.Second)
		defer tick.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case <-tick.C:
				var m runtime.MemStats
				runtime.ReadMemStats(&m)
				stats.Record(ctx, allocStats.M(int64(m.Alloc)))
				stats.Record(ctx, totalAllocStats.M(int64(m.TotalAlloc)))
				stats.Record(ctx, sysStats.M(int64(m.Sys)))
				stats.Record(ctx, gcStats.M(int64(m.NumGC)))

				if maxMemory > 0 && m.Alloc >= maxMemory {
					onceMaxMemorySignal.Do(func() {
						p, err := os.FindProcess(os.Getpid())
						if err != nil {
							log.Error("unable to find current process: %v", err)
							return
						}
						if err := p.Signal(sdk.SIGINFO); err != nil {
							log.Error("unable to send signal: %v", err)
							return
						}
						log.Info("metrics> SIGINFO signal send to %v", os.Getpid())
					})
				}
			}
		}
	})

	return []*view.View{&allocView, &totalAllocView, &sysView, &gcView}
}
<|endoftext|>


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

package go2cpp

import (
	"io"
	"runtime"
	"sync"
	"syscall/js"
)

const (
	readChunkSize = 4096
)

type Context struct {
	v               js.Value
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
}

func NewContext(sampleRate int, channelNum, bitDepthInBytes int) *Context {
	v := js.Global().Get("go2cpp").Call("createAudio", sampleRate, channelNum, bitDepthInBytes)
	return &Context{
		v:               v,
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}
}

func (c *Context) NewPlayer(r io.Reader) *Player {
	cond := sync.NewCond(&sync.Mutex{})
	onwritten := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		cond.Signal()
		return nil
	})
	p := &Player{
		context:   c,
		src:       r,
		volume:    1,
		cond:      cond,
		onWritten: onwritten,
	}
	runtime.SetFinalizer(p, (*Player).Close)
	return p
}

func (c *Context) oneBufferSize() int {
	// TODO: This must be audio.oneBufferSize(p.context.sampleRate). Avoid the duplication.
	return c.sampleRate * c.channelNum * c.bitDepthInBytes / 4
}

func (c *Context) MaxBufferSize() int {
	// TODO: This must be audio.maxBufferSize(p.context.sampleRate). Avoid the duplication.
	return c.oneBufferSize() * 2
}

type playerState int

const (
	playerStatePaused playerState = iota
	playerStatePlaying
	playerStateClosed
)

type Player struct {
	context *Context
	src     io.Reader
	v       js.Value
	state   playerState
	volume  float64
	cond    *sync.Cond
	err     error
	buf     []byte

	onWritten js.Func
}

func (p *Player) Pause() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return
	}
	if !p.v.Truthy() {
		return
	}

	p.v.Call("pause")
	p.state = playerStatePaused
	p.cond.Signal()
}

func (p *Player) Play() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return
	}

	var runloop bool
	if !p.v.Truthy() {
		p.v = p.context.v.Call("createPlayer", p.onWritten)
		runloop = true
	}

	p.v.Set("volume", p.volume)
	p.v.Call("play")

	// Prepare the first data as soon as possible, or the audio can get stuck.
	// TODO: Get the appropriate buffer size from the C++ side.
	if p.buf == nil {
		n := p.context.oneBufferSize()
		if max := p.context.MaxBufferSize() - p.UnplayedBufferSize(); n > max {
			n = max
		}
		p.buf = make([]byte, n)
	}
	n, err := p.src.Read(p.buf)
	if err != nil && err != io.EOF {
		p.setErrorImpl(err)
		return
	}
	if n > 0 {
		dst := js.Global().Get("Uint8Array").New(n)
		p.writeImpl(dst, p.buf[:n])
	}

	if runloop {
		go p.loop()
	}
	p.state = playerStatePlaying
	p.cond.Signal()
}

func (p *Player) IsPlaying() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	return p.state == playerStatePlaying
}

func (p *Player) Reset() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return
	}
	p.state = playerStatePaused

	if !p.v.Truthy() {
		return
	}

	p.v.Call("close", true)
	p.v = js.Undefined()
	p.cond.Signal()
}

func (p *Player) Volume() float64 {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if !p.v.Truthy() {
		return p.volume
	}
	return p.v.Get("volume").Float()
}

func (p *Player) SetVolume(volume float64) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	p.volume = volume
	if !p.v.Truthy() {
		return
	}
	p.v.Set("volume", volume)
}

func (p *Player) UnplayedBufferSize() int {
	if !p.v.Truthy() {
		return 0
	}
	return p.v.Get("unplayedBufferSize").Int()
}

func (p *Player) Err() error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	return p.err
}

func (p *Player) Close() error {
	runtime.SetFinalizer(p, nil)
	return p.close(true)
}

func (p *Player) close(remove bool) error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return p.err
	}

	if p.v.Truthy() {
		p.v.Call("close", false)
		p.v = js.Undefined()
	}
	if remove {
		p.state = playerStateClosed
		p.onWritten.Release()
	} else {
		p.state = playerStatePaused
	}
	p.cond.Signal()
	return p.err
}

func (p *Player) setError(err error) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.setErrorImpl(err)
}

func (p *Player) setErrorImpl(err error) {
	if p.state != playerStateClosed && p.v.Truthy() {
		p.v.Call("close", true)
		p.v = js.Undefined()
	}
	p.err = err
	p.state = playerStateClosed
	p.cond.Signal()
}

func (p *Player) shouldWait() bool {
	if !p.v.Truthy() {
		return false
	}
	switch p.state {
	case playerStatePaused:
		return true
	case playerStatePlaying:
		return p.v.Get("unplayedBufferSize").Int() >= p.context.MaxBufferSize()
	}
	return false
}

func (p *Player) waitUntilUnpaused() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	for p.shouldWait() {
		p.cond.Wait()
	}
	return p.v.Truthy() && p.state == playerStatePlaying
}

func (p *Player) write(dst js.Value, src []byte) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.writeImpl(dst, src)
}

func (p *Player) writeImpl(dst js.Value, src []byte) {
	if p.state == playerStateClosed {
		return
	}
	if !p.v.Truthy() {
		return
	}

	js.CopyBytesToJS(dst, src)
	p.v.Call("write", dst, len(src))
}

func (p *Player) loop() {
	buf := make([]byte, readChunkSize)
	dst := js.Global().Get("Uint8Array").New(readChunkSize)

	for {
		if !p.waitUntilUnpaused() {
			return
		}

		n := readChunkSize
		if max := p.context.MaxBufferSize() - p.UnplayedBufferSize(); n > max {
			n = max
		}
		n2, err := p.src.Read(buf[:n])
		if err != nil && err != io.EOF {
			p.setError(err)
			return
		}
		if n > 0 {
			p.write(dst, buf[:n2])
		}

		if err == io.EOF {
			p.close(false)
			return
		}
	}
}
<commit_msg>audio/internal/go2cpp: Refactoring<commit_after>// Copyright 2021 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package go2cpp

import (
	"io"
	"runtime"
	"sync"
	"syscall/js"
)

type Context struct {
	v               js.Value
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
}

func NewContext(sampleRate int, channelNum, bitDepthInBytes int) *Context {
	v := js.Global().Get("go2cpp").Call("createAudio", sampleRate, channelNum, bitDepthInBytes)
	return &Context{
		v:               v,
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}
}

func (c *Context) NewPlayer(r io.Reader) *Player {
	cond := sync.NewCond(&sync.Mutex{})
	onwritten := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		cond.Signal()
		return nil
	})
	p := &Player{
		context:   c,
		src:       r,
		volume:    1,
		cond:      cond,
		onWritten: onwritten,
	}
	runtime.SetFinalizer(p, (*Player).Close)
	return p
}

func (c *Context) oneBufferSize() int {
	// TODO: This must be audio.oneBufferSize(p.context.sampleRate). Avoid the duplication.
	return c.sampleRate * c.channelNum * c.bitDepthInBytes / 4
}

func (c *Context) MaxBufferSize() int {
	// TODO: This must be audio.maxBufferSize(p.context.sampleRate). Avoid the duplication.
	return c.oneBufferSize() * 2
}

type playerState int

const (
	playerStatePaused playerState = iota
	playerStatePlaying
	playerStateClosed
)

type Player struct {
	context *Context
	src     io.Reader
	v       js.Value
	state   playerState
	volume  float64
	cond    *sync.Cond
	err     error
	buf     []byte

	onWritten js.Func
}

func (p *Player) Pause() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return
	}
	if !p.v.Truthy() {
		return
	}

	p.v.Call("pause")
	p.state = playerStatePaused
	p.cond.Signal()
}

func (p *Player) Play() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return
	}

	var runloop bool
	if !p.v.Truthy() {
		p.v = p.context.v.Call("createPlayer", p.onWritten)
		runloop = true
	}

	p.v.Set("volume", p.volume)
	p.v.Call("play")

	// Prepare the first data as soon as possible, or the audio can get stuck.
	// TODO: Get the appropriate buffer size from the C++ side.
	if p.buf == nil {
		n := p.context.oneBufferSize()
		if max := p.context.MaxBufferSize() - p.UnplayedBufferSize(); n > max {
			n = max
		}
		p.buf = make([]byte, n)
	}
	n, err := p.src.Read(p.buf)
	if err != nil && err != io.EOF {
		p.setErrorImpl(err)
		return
	}
	if n > 0 {
		dst := js.Global().Get("Uint8Array").New(n)
		p.writeImpl(dst, p.buf[:n])
	}

	if runloop {
		go p.loop()
	}
	p.state = playerStatePlaying
	p.cond.Signal()
}

func (p *Player) IsPlaying() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	return p.state == playerStatePlaying
}

func (p *Player) Reset() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return
	}
	p.state = playerStatePaused

	if !p.v.Truthy() {
		return
	}

	p.v.Call("close", true)
	p.v = js.Undefined()
	p.cond.Signal()
}

func (p *Player) Volume() float64 {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if !p.v.Truthy() {
		return p.volume
	}
	return p.v.Get("volume").Float()
}

func (p *Player) SetVolume(volume float64) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	p.volume = volume
	if !p.v.Truthy() {
		return
	}
	p.v.Set("volume", volume)
}

func (p *Player) UnplayedBufferSize() int {
	if !p.v.Truthy() {
		return 0
	}
	return p.v.Get("unplayedBufferSize").Int()
}

func (p *Player) Err() error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	return p.err
}

func (p *Player) Close() error {
	runtime.SetFinalizer(p, nil)
	return p.close(true)
}

func (p *Player) close(remove bool) error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.state == playerStateClosed {
		return p.err
	}

	if p.v.Truthy() {
		p.v.Call("close", false)
		p.v = js.Undefined()
	}
	if remove {
		p.state = playerStateClosed
		p.onWritten.Release()
	} else {
		p.state = playerStatePaused
	}
	p.cond.Signal()
	return p.err
}

func (p *Player) setError(err error) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.setErrorImpl(err)
}

func (p *Player) setErrorImpl(err error) {
	if p.state != playerStateClosed && p.v.Truthy() {
		p.v.Call("close", true)
		p.v = js.Undefined()
	}
	p.err = err
	p.state = playerStateClosed
	p.cond.Signal()
}

func (p *Player) shouldWait() bool {
	if !p.v.Truthy() {
		return false
	}
	switch p.state {
	case playerStatePaused:
		return true
	case playerStatePlaying:
		return p.v.Get("unplayedBufferSize").Int() >= p.context.MaxBufferSize()
	}
	return false
}

func (p *Player) waitUntilUnpaused() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	for p.shouldWait() {
		p.cond.Wait()
	}
	return p.v.Truthy() && p.state == playerStatePlaying
}

func (p *Player) write(dst js.Value, src []byte) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.writeImpl(dst, src)
}

func (p *Player) writeImpl(dst js.Value, src []byte) {
	if p.state == playerStateClosed {
		return
	}
	if !p.v.Truthy() {
		return
	}

	js.CopyBytesToJS(dst, src)
	p.v.Call("write", dst, len(src))
}

func (p *Player) loop() {
	const readChunkSize = 4096

	buf := make([]byte, readChunkSize)
	dst := js.Global().Get("Uint8Array").New(readChunkSize)

	for {
		if !p.waitUntilUnpaused() {
			return
		}

		n := readChunkSize
		if max := p.context.MaxBufferSize() - p.UnplayedBufferSize(); n > max {
			n = max
		}
		n2, err := p.src.Read(buf[:n])
		if err != nil && err != io.EOF {
			p.setError(err)
			return
		}
		if n > 0 {
			p.write(dst, buf[:n2])
		}

		if err == io.EOF {
			p.close(false)
			return
		}
	}
}
<|endoftext|>


<commit_before>package mango

import (
	"http"
	"testing"
	"runtime"
)

func jsonServer(env Env) (Status, Headers, Body) {
	return 200, Headers{"Content-Type": []string{"application/json"}, "Content-Length": []string{"13"}}, Body("{\"foo\":\"bar\"}")
}

func nonJsonServer(env Env) (Status, Headers, Body) {
	return 200, Headers{"Content-Type": []string{"text/html"}}, Body("
Hello World!
")
}

func init() {
	runtime.GOMAXPROCS(4)
}

func TestJSONPSuccess(t *testing.T) {
	// Compile the stack
	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)
	status, headers, body := jsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 200 {
		t.Error("Expected status to equal 200, got:", status)
	}

	if headers.Get("Content-Type") != "application/javascript" {
		t.Error("Expected Content-Type to equal \"application/javascript\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "28" {
		t.Error("Expected Content-Length to equal \"28\", got:", headers.Get("Content-Length"))
	}

	expected := "parseResponse({\"foo\":\"bar\"})"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func TestNonJSONPSuccess(t *testing.T) {
	// Compile the stack
	nonJsonpStack := new(Stack)
	nonJsonpStack.Middleware(JSONP)
	nonJsonpApp := nonJsonpStack.Compile(nonJsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)
	status, headers, body := nonJsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 200 {
		t.Error("Expected status to equal 200, got:", status)
	}

	if headers.Get("Content-Type") != "text/html" {
		t.Error("Expected Content-Type to equal \"text/html\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "" {
		t.Error("Expected Content-Length to equal \"\", got:", headers.Get("Content-Length"))
	}

	expected := "
Hello World!
"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func TestJSONPNoCallback(t *testing.T) {
	// Compile the stack
	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/", nil)
	status, headers, body := jsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 200 {
		t.Error("Expected status to equal 200, got:", status)
	}

	if headers.Get("Content-Type") != "application/json" {
		t.Error("Expected Content-Type to equal \"application/json\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "13" {
		t.Error("Expected Content-Length to equal \"13\", got:", headers.Get("Content-Length"))
	}

	expected := "{\"foo\":\"bar\"}"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func TestJSONPInvalidCallback(t *testing.T) {
	// Compile the stack
	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/?callback=invalid(callback)", nil)
	status, headers, body := jsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 400 {
		t.Error("Expected status to equal 400, got:", status)
	}

	if headers.Get("Content-Type") != "text/plain" {
		t.Error("Expected Content-Type to equal \"text/plain\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "11" {
		t.Error("Expected Content-Length to equal \"11\", got:", headers.Get("Content-Length"))
	}

	expected := "Bad Request"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func BenchmarkJSONP(b *testing.B) {
	b.StopTimer()

	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		jsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}

func BenchmarkNonJSONP(b *testing.B) {
	b.StopTimer()

	nonJsonpStack := new(Stack)
	nonJsonpStack.Middleware(JSONP)
	nonJsonpApp := nonJsonpStack.Compile(nonJsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
    nonJsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}

func BenchmarkJSONPNoCallback(b *testing.B) {
	b.StopTimer()

	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		jsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}

func BenchmarkJSONPInvalidCallback(b *testing.B) {
	b.StopTimer()

	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/?callback=invalid(callback)", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		jsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}
<commit_msg>formatting fix for jsonp_test<commit_after>package mango

import (
	"http"
	"testing"
	"runtime"
)

func jsonServer(env Env) (Status, Headers, Body) {
	return 200, Headers{"Content-Type": []string{"application/json"}, "Content-Length": []string{"13"}}, Body("{\"foo\":\"bar\"}")
}

func nonJsonServer(env Env) (Status, Headers, Body) {
	return 200, Headers{"Content-Type": []string{"text/html"}}, Body("
Hello World!
")
}

func init() {
	runtime.GOMAXPROCS(4)
}

func TestJSONPSuccess(t *testing.T) {
	// Compile the stack
	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)
	status, headers, body := jsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 200 {
		t.Error("Expected status to equal 200, got:", status)
	}

	if headers.Get("Content-Type") != "application/javascript" {
		t.Error("Expected Content-Type to equal \"application/javascript\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "28" {
		t.Error("Expected Content-Length to equal \"28\", got:", headers.Get("Content-Length"))
	}

	expected := "parseResponse({\"foo\":\"bar\"})"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func TestNonJSONPSuccess(t *testing.T) {
	// Compile the stack
	nonJsonpStack := new(Stack)
	nonJsonpStack.Middleware(JSONP)
	nonJsonpApp := nonJsonpStack.Compile(nonJsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)
	status, headers, body := nonJsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 200 {
		t.Error("Expected status to equal 200, got:", status)
	}

	if headers.Get("Content-Type") != "text/html" {
		t.Error("Expected Content-Type to equal \"text/html\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "" {
		t.Error("Expected Content-Length to equal \"\", got:", headers.Get("Content-Length"))
	}

	expected := "
Hello World!
"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func TestJSONPNoCallback(t *testing.T) {
	// Compile the stack
	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/", nil)
	status, headers, body := jsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 200 {
		t.Error("Expected status to equal 200, got:", status)
	}

	if headers.Get("Content-Type") != "application/json" {
		t.Error("Expected Content-Type to equal \"application/json\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "13" {
		t.Error("Expected Content-Length to equal \"13\", got:", headers.Get("Content-Length"))
	}

	expected := "{\"foo\":\"bar\"}"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func TestJSONPInvalidCallback(t *testing.T) {
	// Compile the stack
	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	// Request against it
	request, err := http.NewRequest("GET", "http://localhost:3000/?callback=invalid(callback)", nil)
	status, headers, body := jsonpApp(Env{"mango.request": &Request{request}})

	if err != nil {
		t.Error(err)
	}

	if status != 400 {
		t.Error("Expected status to equal 400, got:", status)
	}

	if headers.Get("Content-Type") != "text/plain" {
		t.Error("Expected Content-Type to equal \"text/plain\", got:", headers.Get("Content-Type"))
	}

	if headers.Get("Content-Length") != "11" {
		t.Error("Expected Content-Length to equal \"11\", got:", headers.Get("Content-Length"))
	}

	expected := "Bad Request"
	if string(body) != expected {
		t.Error("Expected body:", string(body), "to equal:", expected)
	}
}

func BenchmarkJSONP(b *testing.B) {
	b.StopTimer()

	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		jsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}

func BenchmarkNonJSONP(b *testing.B) {
	b.StopTimer()

	nonJsonpStack := new(Stack)
	nonJsonpStack.Middleware(JSONP)
	nonJsonpApp := nonJsonpStack.Compile(nonJsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/?callback=parseResponse", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		nonJsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}

func BenchmarkJSONPNoCallback(b *testing.B) {
	b.StopTimer()

	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		jsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}

func BenchmarkJSONPInvalidCallback(b *testing.B) {
	b.StopTimer()

	jsonpStack := new(Stack)
	jsonpStack.Middleware(JSONP)
	jsonpApp := jsonpStack.Compile(jsonServer)

	request, _ := http.NewRequest("GET", "http://localhost:3000/?callback=invalid(callback)", nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		jsonpApp(Env{"mango.request": &Request{request}})
	}
	b.StopTimer()
}
<|endoftext|>


<commit_before>package fs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"syscall"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-mtpfs/mtp"
)

type classicNode struct {
	mtpNodeImpl

	// local file containing the contents.
	backing string

	// If set, the backing file was changed.
	dirty bool

	// If set, there was some error writing to the backing store;
	// don't flush file to device.
	error fuse.Status
}

func (n *classicNode) send() error {
	if !n.dirty {
		return nil
	}

	if n.backing == "" {
		log.Panicf("sending file without backing store: %q", n.obj.Filename)
	}

	f := n.obj
	if !n.error.Ok() {
		n.dirty = false
		os.Remove(n.backing)
		n.backing = ""
		n.error = fuse.OK
		n.obj.CompressedSize = 0
		n.Size = 0
		log.Printf("not sending file %q due to write errors", f.Filename)
		return syscall.EIO // TODO - send back n.error
	}

	fi, err := os.Stat(n.backing)
	if err != nil {
		log.Printf("could not do stat for send: %v", err)
		return err
	}
	if fi.Size() == 0 {
		log.Printf("cannot send 0 byte file %q", f.Filename)
		return syscall.EINVAL
	}

	if n.obj.Filename == "" {
		return nil
	}
	if n.fs.mungeVfat[n.StorageID()] {
		f.Filename = SanitizeDosName(f.Filename)
	}

	backing, err := os.Open(n.backing)
	if err != nil {
		return err
	}
	defer backing.Close()

	log.Printf("sending file %q to device: %d bytes.", f.Filename, fi.Size())
	if n.Handle() != 0 {
		// Apparently, you can't overwrite things in MTP.
		err := n.fs.dev.DeleteObject(n.Handle())
		if err != nil {
			return err
		}
		n.handle = 0
	}

	if fi.Size() > 0xFFFFFFFF {
		f.CompressedSize = 0xFFFFFFFF
	} else {
		f.CompressedSize = uint32(fi.Size())
	}
	n.Size = fi.Size()
	start := time.Now()

	_, _, handle, err := n.fs.dev.SendObjectInfo(n.StorageID(), f.ParentObject, f)
	if err != nil {
		log.Printf("SendObjectInfo failed %v", err)
		return syscall.EINVAL
	}
	err = n.fs.dev.SendObject(backing, fi.Size())
	if err != nil {
		log.Printf("SendObject failed %v", err)
		return syscall.EINVAL
	}
	dt := time.Now().Sub(start)
	log.Printf("sent %d bytes in %d ms. %.1f MB/s", fi.Size(),
		dt.Nanoseconds()/1e6, 1e3*float64(fi.Size())/float64(dt.Nanoseconds()))
	n.dirty = false
	n.handle = handle

	// We could leave the file for future reading, but the
	// management of free space is a hassle when doing large
	// copies.
	if len(n.Inode().Files(0)) == 1 {
		os.Remove(n.backing)
		n.backing = ""
	}
	return err
}

// Drop backing data if unused. Returns freed up space.
func (n *classicNode) trim() int64 {
	if n.dirty || n.backing == "" || n.Inode().AnyFile() != nil {
		return 0
	}

	fi, err := os.Stat(n.backing)
	if err != nil {
		return 0
	}

	log.Printf("removing local cache for %q, %d bytes", n.obj.Filename, fi.Size())
	err = os.Remove(n.backing)
	if err != nil {
		return 0
	}
	n.backing = ""
	return fi.Size()
}

// PTP supports partial fetch (not exposed in libmtp), but we might as
// well get the whole thing.
func (n *classicNode) fetch() error {
	if n.backing != "" {
		return nil
	}
	sz := n.Size
	if err := n.fs.ensureFreeSpace(sz); err != nil {
		return err
	}

	f, err := ioutil.TempFile(n.fs.options.Dir, "")
	if err != nil {
		return err
	}

	defer f.Close()

	start := time.Now()
	err = n.fs.dev.GetObject(n.Handle(), f)
	dt := time.Now().Sub(start)
	if err == nil {
		n.backing = f.Name()
		n.dirty = false
		log.Printf("fetched %q, %d bytes in %d ms. %.1f MB/s", n.obj.Filename, sz,
			dt.Nanoseconds()/1e6, 1e3*float64(sz)/float64(dt.Nanoseconds()))
	} else {
		log.Printf("error fetching: %v", err)
		err = syscall.EIO
	}

	return err
}

func (n *classicNode) Open(flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	return &pendingFile{
		node: n,
	}, fuse.OK
}

func (n *classicNode) Truncate(file fuse.File, size uint64, context *fuse.Context) (code fuse.Status) {
	if file != nil {
		return file.Truncate(size)
	} else if n.backing != "" {
		return fuse.ToStatus(os.Truncate(n.backing, int64(size)))
	}
	return fuse.OK
}

////////////////
// writing files.

type pendingFile struct {
	fuse.DefaultFile
	flags    uint32
	loopback *fuse.LoopbackFile
	node     *classicNode
}

func (p *pendingFile) rwLoopback() (*fuse.LoopbackFile, fuse.Status) {
	if p.loopback == nil {
		err := p.node.fetch()
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}

		p.loopback = &fuse.LoopbackFile{File: f}
	}
	return p.loopback, fuse.OK
}

func (p *pendingFile) Read(data []byte, off int64) (fuse.ReadResult, fuse.Status) {
	if p.loopback == nil {
		if err := p.node.fetch(); err != nil {
			log.Printf("fetch failed: %v", err)
			return nil, fuse.EIO
		}
		f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		p.loopback = &fuse.LoopbackFile{File: f}
	}
	return p.loopback.Read(data, off)
}

func (p *pendingFile) Write(data []byte, off int64) (uint32, fuse.Status) {
	p.node.dirty = true
	f, code := p.rwLoopback()
	if !code.Ok() {
		return 0, code
	}

	n, code := f.Write(data, off)
	if !code.Ok() {
		p.node.error = code
	}
	return n, code
}

func (p *pendingFile) Truncate(size uint64) fuse.Status {
	f, code := p.rwLoopback()
	if !code.Ok() {
		return code
	}

	code = f.Truncate(size)
	if !code.Ok() {
		return code
	}
	p.node.dirty = true
	if code.Ok() && size == 0 {
		p.node.error = fuse.OK
	}
	return code
}

func (p *pendingFile) Flush() fuse.Status {
	if p.loopback == nil {
		return fuse.OK
	}
	code := p.loopback.Flush()
	if !code.Ok() {
		return code
	}

	s := fuse.ToStatus(p.node.send())
	if s == fuse.ENOSYS {
		return fuse.EIO
	}
	return s
}

func (p *pendingFile) Release() {
	if p.loopback != nil {
		p.loopback.Release()
	}
}

////////////////////////////////////////////////////////////////

func (fs *DeviceFs) trimUnused(todo int64, node *fuse.Inode) (done int64) {
	for _, ch := range node.Children() {
		if done > todo {
			break
		}

		if fn, ok := ch.FsNode().(*classicNode); ok {
			done += fn.trim()
		} else if ch.IsDir() {
			done += fs.trimUnused(todo-done, ch)
		}
	}
	return
}

func (fs *DeviceFs) freeBacking() (int64, error) {
	t := syscall.Statfs_t{}
	err := syscall.Statfs(fs.options.Dir, &t)
	if err != nil {
		return 0, err
	}

	return int64(t.Bfree * uint64(t.Bsize)), nil
}

func (fs *DeviceFs) ensureFreeSpace(want int64) error {
	free, err := fs.freeBacking()
	if err != nil {
		return err
	}
	if free > want {
		return nil
	}

	todo := want - free + 10*1024
	fs.trimUnused(todo, fs.root.Inode())

	free, err = fs.freeBacking()
	if err != nil {
		return err
	}
	if free > want {
		return nil
	}

	return fmt.Errorf("not enough space. Have %d, want %d", free, want)
}

func (fs *DeviceFs) setupClassic() error {
	if fs.options.Dir == "" {
		var err error
		fs.options.Dir, err = ioutil.TempDir(os.TempDir(), "go-mtpfs")
		if err != nil {
			return err
		}
		fs.delBackingDir = true
	}
	if fi, err := os.Lstat(fs.options.Dir); err != nil || !fi.IsDir() {
		return fmt.Errorf("%s is not a directory")
	}
	return nil
}

func (fs *DeviceFs) OnUnmount() {
	if fs.delBackingDir {
		os.RemoveAll(fs.options.Dir)
	}
}

func (fs *DeviceFs) createClassicFile(obj mtp.ObjectInfo) (file fuse.File, node fuse.FsNode, err error) {
	backingFile, err := ioutil.TempFile(fs.options.Dir, "")
	cl := &classicNode{
		mtpNodeImpl: mtpNodeImpl{
			obj: &obj,
			fs:  fs,
		},
		dirty:   true,
		backing: backingFile.Name(),
	}
	file = &pendingFile{
		loopback: &fuse.LoopbackFile{File: backingFile},
		node:     cl,
	}

	node = cl
	return
}
<commit_msg>Make free space error message more verbose.<commit_after>package fs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"syscall"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-mtpfs/mtp"
)

type classicNode struct {
	mtpNodeImpl

	// local file containing the contents.
	backing string

	// If set, the backing file was changed.
	dirty bool

	// If set, there was some error writing to the backing store;
	// don't flush file to device.
	error fuse.Status
}

func (n *classicNode) send() error {
	if !n.dirty {
		return nil
	}

	if n.backing == "" {
		log.Panicf("sending file without backing store: %q", n.obj.Filename)
	}

	f := n.obj
	if !n.error.Ok() {
		n.dirty = false
		os.Remove(n.backing)
		n.backing = ""
		n.error = fuse.OK
		n.obj.CompressedSize = 0
		n.Size = 0
		log.Printf("not sending file %q due to write errors", f.Filename)
		return syscall.EIO // TODO - send back n.error
	}

	fi, err := os.Stat(n.backing)
	if err != nil {
		log.Printf("could not do stat for send: %v", err)
		return err
	}
	if fi.Size() == 0 {
		log.Printf("cannot send 0 byte file %q", f.Filename)
		return syscall.EINVAL
	}

	if n.obj.Filename == "" {
		return nil
	}
	if n.fs.mungeVfat[n.StorageID()] {
		f.Filename = SanitizeDosName(f.Filename)
	}

	backing, err := os.Open(n.backing)
	if err != nil {
		return err
	}
	defer backing.Close()

	log.Printf("sending file %q to device: %d bytes.", f.Filename, fi.Size())
	if n.Handle() != 0 {
		// Apparently, you can't overwrite things in MTP.
		err := n.fs.dev.DeleteObject(n.Handle())
		if err != nil {
			return err
		}
		n.handle = 0
	}

	if fi.Size() > 0xFFFFFFFF {
		f.CompressedSize = 0xFFFFFFFF
	} else {
		f.CompressedSize = uint32(fi.Size())
	}
	n.Size = fi.Size()
	start := time.Now()

	_, _, handle, err := n.fs.dev.SendObjectInfo(n.StorageID(), f.ParentObject, f)
	if err != nil {
		log.Printf("SendObjectInfo failed %v", err)
		return syscall.EINVAL
	}
	err = n.fs.dev.SendObject(backing, fi.Size())
	if err != nil {
		log.Printf("SendObject failed %v", err)
		return syscall.EINVAL
	}
	dt := time.Now().Sub(start)
	log.Printf("sent %d bytes in %d ms. %.1f MB/s", fi.Size(),
		dt.Nanoseconds()/1e6, 1e3*float64(fi.Size())/float64(dt.Nanoseconds()))
	n.dirty = false
	n.handle = handle

	// We could leave the file for future reading, but the
	// management of free space is a hassle when doing large
	// copies.
	if len(n.Inode().Files(0)) == 1 {
		os.Remove(n.backing)
		n.backing = ""
	}
	return err
}

// Drop backing data if unused. Returns freed up space.
func (n *classicNode) trim() int64 {
	if n.dirty || n.backing == "" || n.Inode().AnyFile() != nil {
		return 0
	}

	fi, err := os.Stat(n.backing)
	if err != nil {
		return 0
	}

	log.Printf("removing local cache for %q, %d bytes", n.obj.Filename, fi.Size())
	err = os.Remove(n.backing)
	if err != nil {
		return 0
	}
	n.backing = ""
	return fi.Size()
}

// PTP supports partial fetch (not exposed in libmtp), but we might as
// well get the whole thing.
func (n *classicNode) fetch() error {
	if n.backing != "" {
		return nil
	}
	sz := n.Size
	if err := n.fs.ensureFreeSpace(sz); err != nil {
		return err
	}

	f, err := ioutil.TempFile(n.fs.options.Dir, "")
	if err != nil {
		return err
	}

	defer f.Close()

	start := time.Now()
	err = n.fs.dev.GetObject(n.Handle(), f)
	dt := time.Now().Sub(start)
	if err == nil {
		n.backing = f.Name()
		n.dirty = false
		log.Printf("fetched %q, %d bytes in %d ms. %.1f MB/s", n.obj.Filename, sz,
			dt.Nanoseconds()/1e6, 1e3*float64(sz)/float64(dt.Nanoseconds()))
	} else {
		log.Printf("error fetching: %v", err)
		err = syscall.EIO
	}

	return err
}

func (n *classicNode) Open(flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	return &pendingFile{
		node: n,
	}, fuse.OK
}

func (n *classicNode) Truncate(file fuse.File, size uint64, context *fuse.Context) (code fuse.Status) {
	if file != nil {
		return file.Truncate(size)
	} else if n.backing != "" {
		return fuse.ToStatus(os.Truncate(n.backing, int64(size)))
	}
	return fuse.OK
}

////////////////
// writing files.

type pendingFile struct {
	fuse.DefaultFile
	flags    uint32
	loopback *fuse.LoopbackFile
	node     *classicNode
}

func (p *pendingFile) rwLoopback() (*fuse.LoopbackFile, fuse.Status) {
	if p.loopback == nil {
		err := p.node.fetch()
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}

		p.loopback = &fuse.LoopbackFile{File: f}
	}
	return p.loopback, fuse.OK
}

func (p *pendingFile) Read(data []byte, off int64) (fuse.ReadResult, fuse.Status) {
	if p.loopback == nil {
		if err := p.node.fetch(); err != nil {
			log.Printf("fetch failed: %v", err)
			return nil, fuse.EIO
		}
		f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		p.loopback = &fuse.LoopbackFile{File: f}
	}
	return p.loopback.Read(data, off)
}

func (p *pendingFile) Write(data []byte, off int64) (uint32, fuse.Status) {
	p.node.dirty = true
	f, code := p.rwLoopback()
	if !code.Ok() {
		return 0, code
	}

	n, code := f.Write(data, off)
	if !code.Ok() {
		p.node.error = code
	}
	return n, code
}

func (p *pendingFile) Truncate(size uint64) fuse.Status {
	f, code := p.rwLoopback()
	if !code.Ok() {
		return code
	}

	code = f.Truncate(size)
	if !code.Ok() {
		return code
	}
	p.node.dirty = true
	if code.Ok() && size == 0 {
		p.node.error = fuse.OK
	}
	return code
}

func (p *pendingFile) Flush() fuse.Status {
	if p.loopback == nil {
		return fuse.OK
	}
	code := p.loopback.Flush()
	if !code.Ok() {
		return code
	}

	s := fuse.ToStatus(p.node.send())
	if s == fuse.ENOSYS {
		return fuse.EIO
	}
	return s
}

func (p *pendingFile) Release() {
	if p.loopback != nil {
		p.loopback.Release()
	}
}

////////////////////////////////////////////////////////////////

func (fs *DeviceFs) trimUnused(todo int64, node *fuse.Inode) (done int64) {
	for _, ch := range node.Children() {
		if done > todo {
			break
		}

		if fn, ok := ch.FsNode().(*classicNode); ok {
			done += fn.trim()
		} else if ch.IsDir() {
			done += fs.trimUnused(todo-done, ch)
		}
	}
	return
}

func (fs *DeviceFs) freeBacking() (int64, error) {
	t := syscall.Statfs_t{}
	err := syscall.Statfs(fs.options.Dir, &t)
	if err != nil {
		return 0, err
	}

	return int64(t.Bfree * uint64(t.Bsize)), nil
}

func (fs *DeviceFs) ensureFreeSpace(want int64) error {
	free, err := fs.freeBacking()
	if err != nil {
		return err
	}
	if free > want {
		return nil
	}

	todo := want - free + 10*1024
	fs.trimUnused(todo, fs.root.Inode())

	free, err = fs.freeBacking()
	if err != nil {
		return err
	}
	if free > want {
		return nil
	}

	return fmt.Errorf("not enough space in %s. Have %d, want %d", fs.options.Dir, free, want)
}

func (fs *DeviceFs) setupClassic() error {
	if fs.options.Dir == "" {
		var err error
		fs.options.Dir, err = ioutil.TempDir(os.TempDir(), "go-mtpfs")
		if err != nil {
			return err
		}
		fs.delBackingDir = true
	}
	if fi, err := os.Lstat(fs.options.Dir); err != nil || !fi.IsDir() {
		return fmt.Errorf("%s is not a directory")
	}
	return nil
}

func (fs *DeviceFs) OnUnmount() {
	if fs.delBackingDir {
		os.RemoveAll(fs.options.Dir)
	}
}

func (fs *DeviceFs) createClassicFile(obj mtp.ObjectInfo) (file fuse.File, node fuse.FsNode, err error) {
	backingFile, err := ioutil.TempFile(fs.options.Dir, "")
	cl := &classicNode{
		mtpNodeImpl: mtpNodeImpl{
			obj: &obj,
			fs:  fs,
		},
		dirty:   true,
		backing: backingFile.Name(),
	}
	file = &pendingFile{
		loopback: &fuse.LoopbackFile{File: backingFile},
		node:     cl,
	}

	node = cl
	return
}
<|endoftext|>