<commit_before>package eval

import (
	"bytes"
	"monkey/ast"
	"strings"
)

type Array struct {
	Members []Object
}

func (a *Array) Inspect() string {
	var out bytes.Buffer
	members := []string{}
	for _, m := range a.Members {
		members = append(members, m.Inspect())
	}
	out.WriteString("[")
	out.WriteString(strings.Join(members, ", "))
	out.WriteString("]")

	return out.String()
}
func (a *Array) Type() ObjectType { return ARRAY_OBJ }

func (a *Array) CallMethod(method string, args []Object) Object {
	if method == "methods" {
		return a.methods()
	}
	m, ok := arrayMethods[method]
	if !ok {
		return newError(NOMETHODERROR, method, a.Type())
	}
	return m(a, args...)
}

func (a *Array) methods() Object {
	methods := &Array{}
	for key, _ := range arrayMethods {
		m := &String{Value: key}
		methods.Members = append(methods.Members, m)
	}
	return methods
}

var arrayMethods = map[string]func(a *Array, args ...Object) Object{
	"pop": func(a *Array, args ...Object) Object {
		last := len(a.Members) - 1
		if len(args) == 0 {
			if last < 0 {
				return newError(INDEXERROR, last)
			}
			popped := a.Members[last]
			a.Members = a.Members[:last]
			return popped
		}
		idx := args[0].(*Integer).Value
		if idx < 0 {
			idx = idx + int64(last+1)
		}
		if idx < 0 || idx > int64(last) {
			return newError(INDEXERROR, idx)
		}
		popped := a.Members[idx]
		a.Members = append(a.Members[:idx], a.Members[idx:]...)
		return popped
	},
	"push": func(a *Array, args ...Object) Object {
		l := len(args)
		if l != 1 {
			return newError(ARGUMENTERROR, "1", l)
		}
		a.Members = append(a.Members, args[0])
		return a
	},
	"map": func(a *Array, args ...Object) Object {
		if len(args) != 1 {
			return newError(ARGUMENTERROR, "1", len(args))
		}
		block, ok := args[0].(*Function)
		if !ok {
			return newError(INPUTERROR, args[0].Type(), "map")
		}
		arr := &Array{}
		arr.Members = []Object{}
		s := NewScope(nil)
		for _, argument := range a.Members {
			s.Set(block.Literal.Parameters[0].(*ast.Identifier).Value, argument)
			r := Eval(block.Literal.Body, s)
			if obj, ok := r.(*ReturnValue); ok {
				r = obj.Value
			}
			arr.Members = append(arr.Members, r)
		}
		return arr
	},
	"filter": func(a *Array, args ...Object) Object {
		if len(args) != 1 {
			return newError(ARGUMENTERROR, "1", len(args))
		}
		block, ok := args[0].(*Function)
		if !ok {
			return newError(INPUTERROR, args[0].Type(), "filter")
		}
		arr := &Array{}
		arr.Members = []Object{}
		s := NewScope(nil)
		for _, argument := range a.Members {
			s.Set(block.Literal.Parameters[0].(*ast.Identifier).Value, argument)
			r, ok := Eval(block.Literal.Body, s).(*Boolean)
			if !ok {
				return newError(RTERROR, "BOOLEAN")
			}
			if r.Value {
				arr.Members = append(arr.Members, argument)
			}
		}
		return arr
	},
}
<commit_msg>fixed pop logic<commit_after>package eval

import (
	"bytes"
	"monkey/ast"
	"strings"
)

type Array struct {
	Members []Object
}

func (a *Array) Inspect() string {
	var out bytes.Buffer
	members := []string{}
	for _, m := range a.Members {
		members = append(members, m.Inspect())
	}
	out.WriteString("[")
	out.WriteString(strings.Join(members, ", "))
	out.WriteString("]")

	return out.String()
}
func (a *Array) Type() ObjectType { return ARRAY_OBJ }

func (a *Array) CallMethod(method string, args []Object) Object {
	if method == "methods" {
		return a.methods()
	}
	m, ok := arrayMethods[method]
	if !ok {
		return newError(NOMETHODERROR, method, a.Type())
	}
	return m(a, args...)
}

func (a *Array) methods() Object {
	methods := &Array{}
	for key, _ := range arrayMethods {
		m := &String{Value: key}
		methods.Members = append(methods.Members, m)
	}
	return methods
}

var arrayMethods = map[string]func(a *Array, args ...Object) Object{
	"pop": func(a *Array, args ...Object) Object {
		last := len(a.Members) - 1
		if len(args) == 0 {
			if last < 0 {
				return newError(INDEXERROR, last)
			}
			popped := a.Members[last]
			a.Members = a.Members[:last]
			return popped
		}
		idx := args[0].(*Integer).Value
		if idx < 0 {
			idx = idx + int64(last+1)
		}
		if idx < 0 || idx > int64(last) {
			return newError(INDEXERROR, idx)
		}
		popped := a.Members[idx]
		a.Members = append(a.Members[:idx], a.Members[idx+1:]...)
		return popped
	},
	"push": func(a *Array, args ...Object) Object {
		l := len(args)
		if l != 1 {
			return newError(ARGUMENTERROR, "1", l)
		}
		a.Members = append(a.Members, args[0])
		return a
	},
	"map": func(a *Array, args ...Object) Object {
		if len(args) != 1 {
			return newError(ARGUMENTERROR, "1", len(args))
		}
		block, ok := args[0].(*Function)
		if !ok {
			return newError(INPUTERROR, args[0].Type(), "map")
		}
		arr := &Array{}
		arr.Members = []Object{}
		s := NewScope(nil)
		for _, argument := range a.Members {
			s.Set(block.Literal.Parameters[0].(*ast.Identifier).Value, argument)
			r := Eval(block.Literal.Body, s)
			if obj, ok := r.(*ReturnValue); ok {
				r = obj.Value
			}
			arr.Members = append(arr.Members, r)
		}
		return arr
	},
	"filter": func(a *Array, args ...Object) Object {
		if len(args) != 1 {
			return newError(ARGUMENTERROR, "1", len(args))
		}
		block, ok := args[0].(*Function)
		if !ok {
			return newError(INPUTERROR, args[0].Type(), "filter")
		}
		arr := &Array{}
		arr.Members = []Object{}
		s := NewScope(nil)
		for _, argument := range a.Members {
			s.Set(block.Literal.Parameters[0].(*ast.Identifier).Value, argument)
			r, ok := Eval(block.Literal.Body, s).(*Boolean)
			if !ok {
				return newError(RTERROR, "BOOLEAN")
			}
			if r.Value {
				arr.Members = append(arr.Members, argument)
			}
		}
		return arr
	},
}
<|endoftext|>


<commit_before>package models

import (
	"encoding/json"
	"log"
	"reflect"
	"time"
)

// めんどい // type EventType string
const (
	JOIN      = "join"
	LEAVE     = "leave"
	MESSAGE   = "message"
	STAMPRIZE = "stamprize"
	STAMPUSE  = "stampuse"
	MUTE      = "mute"
	UNMUTE    = "unmute"
	AMESH     = "amesh"
)

// Event ...
type Event struct {
	Type      string                 `json:"type"`      // このイベントの種別
	Raw       string                 `json:"raw"`       // このイベントの内容をjson stringにしたもの
	Value     interface{}            `json:"value"`     // このイベントの内容
	Params    map[string]interface{} `json:"params"`    // なんか
	Timestamp int64                  `json:"timestamp"` // このイベントのpublish時間
	User      *User                  `json:"user"`      // このイベントの発起人
}

// ConstructEvent ...
func ConstructEvent(user *User, raw string) (*Event, error) {
	event := new(Event)
	if err := json.Unmarshal([]byte(raw), event); err != nil {
		return event, err
	}
	event.Timestamp = time.Now().UnixNano()
	event.User = user
	switch event.Type {
	case MESSAGE:
		event.Value = map[string]interface{}{
			"text": event.Raw,
		}
	case STAMPRIZE:
		stamprized := new(Event) // stamprizeされたほうの内容
		if err := json.Unmarshal([]byte(event.Raw), stamprized); err != nil {
			log.Println("stamprize error", err)
		}
		event.Value = stamprized
	case STAMPUSE:
		// messageに偽装<s>する</s>しない
		// stampuseされたほうの内容
		stampused := new(Event)
		if err := json.Unmarshal([]byte(event.Raw), stampused); err != nil {
			log.Println("stampuse error", err)
		}
		// TimestampとUserだけ偽造しよう
		stampused.Timestamp = event.Timestamp
		stampused.User = event.User
		event.Value = stampused
		log.Printf("%+v\n", event.Value)
	case MUTE, UNMUTE:
		muted := new(Event)
		if err := json.Unmarshal([]byte(event.Raw), muted); err != nil {
			log.Println("mute/unmute error", err)
		}
		event.Value = muted
	case JOIN:
	}
	return event, nil
}

// StamplyEqual ...
func (ev *Event) StamplyEqual(target *Event) bool {
	stamprized0, ok := ev.Value.(*Event)
	if !ok {
		return false
	}
	stamprized1, ok := target.Value.(*Event)
	if !ok {
		return false
	}
	return reflect.DeepEqual(stamprized0.Value, stamprized1.Value)
}

// NewMessage construct MESSAGE event.
func NewMessage(user *User, text string) *Event {
	return &Event{
		User: user,
		Type: MESSAGE,
		Raw:  text,
		Value: map[string]interface{}{
			"text": text,
		},
		Timestamp: time.Now().UnixNano(),
	}
}
<commit_msg>Remove unnecessary log<commit_after>package models

import (
	"encoding/json"
	"log"
	"reflect"
	"time"
)

// めんどい // type EventType string
const (
	JOIN      = "join"
	LEAVE     = "leave"
	MESSAGE   = "message"
	STAMPRIZE = "stamprize"
	STAMPUSE  = "stampuse"
	MUTE      = "mute"
	UNMUTE    = "unmute"
	AMESH     = "amesh"
)

// Event ...
type Event struct {
	Type      string                 `json:"type"`      // このイベントの種別
	Raw       string                 `json:"raw"`       // このイベントの内容をjson stringにしたもの
	Value     interface{}            `json:"value"`     // このイベントの内容
	Params    map[string]interface{} `json:"params"`    // なんか
	Timestamp int64                  `json:"timestamp"` // このイベントのpublish時間
	User      *User                  `json:"user"`      // このイベントの発起人
}

// ConstructEvent ...
func ConstructEvent(user *User, raw string) (*Event, error) {
	event := new(Event)
	if err := json.Unmarshal([]byte(raw), event); err != nil {
		return event, err
	}
	event.Timestamp = time.Now().UnixNano()
	event.User = user
	switch event.Type {
	case MESSAGE:
		event.Value = map[string]interface{}{
			"text": event.Raw,
		}
	case STAMPRIZE:
		stamprized := new(Event) // stamprizeされたほうの内容
		if err := json.Unmarshal([]byte(event.Raw), stamprized); err != nil {
			log.Println("stamprize error", err)
		}
		event.Value = stamprized
	case STAMPUSE:
		// messageに偽装<s>する</s>しない
		// stampuseされたほうの内容
		stampused := new(Event)
		if err := json.Unmarshal([]byte(event.Raw), stampused); err != nil {
			log.Println("stampuse error", err)
		}
		// TimestampとUserだけ偽造しよう
		stampused.Timestamp = event.Timestamp
		stampused.User = event.User
		event.Value = stampused
	case MUTE, UNMUTE:
		muted := new(Event)
		if err := json.Unmarshal([]byte(event.Raw), muted); err != nil {
			log.Println("mute/unmute error", err)
		}
		event.Value = muted
	case JOIN:
	}
	return event, nil
}

// StamplyEqual ...
func (ev *Event) StamplyEqual(target *Event) bool {
	stamprized0, ok := ev.Value.(*Event)
	if !ok {
		return false
	}
	stamprized1, ok := target.Value.(*Event)
	if !ok {
		return false
	}
	return reflect.DeepEqual(stamprized0.Value, stamprized1.Value)
}

// NewMessage construct MESSAGE event.
func NewMessage(user *User, text string) *Event {
	return &Event{
		User: user,
		Type: MESSAGE,
		Raw:  text,
		Value: map[string]interface{}{
			"text": text,
		},
		Timestamp: time.Now().UnixNano(),
	}
}
<|endoftext|>


<commit_before>// go generate gen.go
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT

package stun

// Session Traversal Utilities for NAT (STUN) Parameters, STUN Methods, Updated: 2016-04-20
const (
	MethodBinding           Method = 0x001 // Binding
	MethodAllocate          Method = 0x003 // Allocate
	MethodRefresh           Method = 0x004 // Refresh
	MethodSend              Method = 0x006 // Send
	MethodData              Method = 0x007 // Data
	MethodCreatePermission  Method = 0x008 // CreatePermission
	MethodChannelBind       Method = 0x009 // ChannelBind
	MethodConnect           Method = 0x00A // Connect
	MethodConnectionBind    Method = 0x00B // ConnectionBind
	MethodConnectionAttempt Method = 0x00C // ConnectionAttempt
)

var methods = map[Method]string{
	0x001: "binding",
	0x003: "allocate",
	0x004: "refresh",
	0x006: "send",
	0x007: "data",
	0x008: "create permission",
	0x009: "channel bind",
	0x00A: "connect",
	0x00B: "connection bind",
	0x00C: "connection attempt",
}

// Session Traversal Utilities for NAT (STUN) Parameters, STUN Attributes, Updated: 2016-04-20
const (
	attrMAPPED_ADDRESS            = 0x0001 // MAPPED-ADDRESS
	attrCHANGE_REQUEST            = 0x0003 // CHANGE-REQUEST
	attrUSERNAME                  = 0x0006 // USERNAME
	attrMESSAGE_INTEGRITY         = 0x0008 // MESSAGE-INTEGRITY
	attrERROR_CODE                = 0x0009 // ERROR-CODE
	attrUNKNOWN_ATTRIBUTES        = 0x000A // UNKNOWN-ATTRIBUTES
	attrCHANNEL_NUMBER            = 0x000C // CHANNEL-NUMBER
	attrLIFETIME                  = 0x000D // LIFETIME
	attrXOR_PEER_ADDRESS          = 0x0012 // XOR-PEER-ADDRESS
	attrDATA                      = 0x0013 // DATA
	attrREALM                     = 0x0014 // REALM
	attrNONCE                     = 0x0015 // NONCE
	attrXOR_RELAYED_ADDRESS       = 0x0016 // XOR-RELAYED-ADDRESS
	attrREQUESTED_ADDRESS_FAMILY  = 0x0017 // REQUESTED-ADDRESS-FAMILY
	attrEVEN_PORT                 = 0x0018 // EVEN-PORT
	attrREQUESTED_TRANSPORT       = 0x0019 // REQUESTED-TRANSPORT
	attrDONT_FRAGMENT             = 0x001A // DONT-FRAGMENT
	attrACCESS_TOKEN              = 0x001B // ACCESS-TOKEN
	attrXOR_MAPPED_ADDRESS        = 0x0020 // XOR-MAPPED-ADDRESS
	attrRESERVATION_TOKEN         = 0x0022 // RESERVATION-TOKEN
	attrPRIORITY                  = 0x0024 // PRIORITY
	attrUSE_CANDIDATE             = 0x0025 // USE-CANDIDATE
	attrPADDING                   = 0x0026 // PADDING
	attrRESPONSE_PORT             = 0x0027 // RESPONSE-PORT
	attrCONNECTION_ID             = 0x002A // CONNECTION-ID
	attrSOFTWARE                  = 0x8022 // SOFTWARE
	attrALTERNATE_SERVER          = 0x8023 // ALTERNATE-SERVER
	attrCACHE_TIMEOUT             = 0x8027 // CACHE-TIMEOUT
	attrFINGERPRINT               = 0x8028 // FINGERPRINT
	attrICE_CONTROLLED            = 0x8029 // ICE-CONTROLLED
	attrICE_CONTROLLING           = 0x802A // ICE-CONTROLLING
	attrRESPONSE_ORIGIN           = 0x802B // RESPONSE-ORIGIN
	attrOTHER_ADDRESS             = 0x802C // OTHER-ADDRESS
	attrECN_CHECK_STUN            = 0x802D // ECN-CHECK STUN
	attrTHIRD_PARTY_AUTHORIZATION = 0x802E // THIRD-PARTY-AUTHORIZATION
	attrCISCO_STUN_FLOWDATA       = 0xC000 // CISCO-STUN-FLOWDATA
	attrENF_FLOW_DESCRIPTION      = 0xC001 // ENF-FLOW-DESCRIPTION
	attrENF_NETWORK_STATUS        = 0xC002 // ENF-NETWORK-STATUS
)

// Session Traversal Utilities for NAT (STUN) Parameters, STUN Error Codes, Updated: 2016-04-20
const (
	StatusTryAlternate                 = 300 // Try Alternate
	StatusBadRequest                   = 400 // Bad Request
	StatusUnauthorized                 = 401 // Unauthorized
	StatusForbidden                    = 403 // Forbidden
	StatusUnknownAttribute             = 420 // Unknown Attribute
	StatusAllocationMismatch           = 437 // Allocation Mismatch
	StatusStaleNonce                   = 438 // Stale Nonce
	StatusAddressFamilynotSupported    = 440 // Address Family not Supported
	StatusWrongCredentials             = 441 // Wrong Credentials
	StatusUnsupportedTransportProtocol = 442 // Unsupported Transport Protocol
	StatusPeerAddressFamilyMismatch    = 443 // Peer Address Family Mismatch
	StatusConnectionAlreadyExists      = 446 // Connection Already Exists
	StatusConnectionTimeoutorFailure   = 447 // Connection Timeout or Failure
	StatusAllocationQuotaReached       = 486 // Allocation Quota Reached
	StatusRoleConflict                 = 487 // Role Conflict
	StatusServerError                  = 500 // Server Error
	StatusInsufficientCapacity         = 508 // Insufficient Capacity
)
<commit_msg>stun: update IANA constants<commit_after>// go generate gen.go
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT

package stun

// Session Traversal Utilities for NAT (STUN) Parameters, STUN Methods, Updated: 2016-08-25
const (
	MethodBinding           Method = 0x001 // Binding
	MethodAllocate          Method = 0x003 // Allocate
	MethodRefresh           Method = 0x004 // Refresh
	MethodSend              Method = 0x006 // Send
	MethodData              Method = 0x007 // Data
	MethodCreatePermission  Method = 0x008 // CreatePermission
	MethodChannelBind       Method = 0x009 // ChannelBind
	MethodConnect           Method = 0x00A // Connect
	MethodConnectionBind    Method = 0x00B // ConnectionBind
	MethodConnectionAttempt Method = 0x00C // ConnectionAttempt
)

var methods = map[Method]string{
	0x001: "binding",
	0x003: "allocate",
	0x004: "refresh",
	0x006: "send",
	0x007: "data",
	0x008: "create permission",
	0x009: "channel bind",
	0x00A: "connect",
	0x00B: "connection bind",
	0x00C: "connection attempt",
}

// Session Traversal Utilities for NAT (STUN) Parameters, STUN Attributes, Updated: 2016-08-25
const (
	attrMAPPED_ADDRESS               = 0x0001 // MAPPED-ADDRESS
	attrCHANGE_REQUEST               = 0x0003 // CHANGE-REQUEST
	attrUSERNAME                     = 0x0006 // USERNAME
	attrMESSAGE_INTEGRITY            = 0x0008 // MESSAGE-INTEGRITY
	attrERROR_CODE                   = 0x0009 // ERROR-CODE
	attrUNKNOWN_ATTRIBUTES           = 0x000A // UNKNOWN-ATTRIBUTES
	attrCHANNEL_NUMBER               = 0x000C // CHANNEL-NUMBER
	attrLIFETIME                     = 0x000D // LIFETIME
	attrXOR_PEER_ADDRESS             = 0x0012 // XOR-PEER-ADDRESS
	attrDATA                         = 0x0013 // DATA
	attrREALM                        = 0x0014 // REALM
	attrNONCE                        = 0x0015 // NONCE
	attrXOR_RELAYED_ADDRESS          = 0x0016 // XOR-RELAYED-ADDRESS
	attrREQUESTED_ADDRESS_FAMILY     = 0x0017 // REQUESTED-ADDRESS-FAMILY
	attrEVEN_PORT                    = 0x0018 // EVEN-PORT
	attrREQUESTED_TRANSPORT          = 0x0019 // REQUESTED-TRANSPORT
	attrDONT_FRAGMENT                = 0x001A // DONT-FRAGMENT
	attrACCESS_TOKEN                 = 0x001B // ACCESS-TOKEN
	attrXOR_MAPPED_ADDRESS           = 0x0020 // XOR-MAPPED-ADDRESS
	attrRESERVATION_TOKEN            = 0x0022 // RESERVATION-TOKEN
	attrPRIORITY                     = 0x0024 // PRIORITY
	attrUSE_CANDIDATE                = 0x0025 // USE-CANDIDATE
	attrPADDING                      = 0x0026 // PADDING
	attrRESPONSE_PORT                = 0x0027 // RESPONSE-PORT
	attrCONNECTION_ID                = 0x002A // CONNECTION-ID
	attrSOFTWARE                     = 0x8022 // SOFTWARE
	attrALTERNATE_SERVER             = 0x8023 // ALTERNATE-SERVER
	attrTRANSACTION_TRANSMIT_COUNTER = 0x8025 // TRANSACTION_TRANSMIT_COUNTER
	attrCACHE_TIMEOUT                = 0x8027 // CACHE-TIMEOUT
	attrFINGERPRINT                  = 0x8028 // FINGERPRINT
	attrICE_CONTROLLED               = 0x8029 // ICE-CONTROLLED
	attrICE_CONTROLLING              = 0x802A // ICE-CONTROLLING
	attrRESPONSE_ORIGIN              = 0x802B // RESPONSE-ORIGIN
	attrOTHER_ADDRESS                = 0x802C // OTHER-ADDRESS
	attrECN_CHECK_STUN               = 0x802D // ECN-CHECK STUN
	attrTHIRD_PARTY_AUTHORIZATION    = 0x802E // THIRD-PARTY-AUTHORIZATION
	attrCISCO_STUN_FLOWDATA          = 0xC000 // CISCO-STUN-FLOWDATA
	attrENF_FLOW_DESCRIPTION         = 0xC001 // ENF-FLOW-DESCRIPTION
	attrENF_NETWORK_STATUS           = 0xC002 // ENF-NETWORK-STATUS
)

// Session Traversal Utilities for NAT (STUN) Parameters, STUN Error Codes, Updated: 2016-08-25
const (
	StatusTryAlternate                 = 300 // Try Alternate
	StatusBadRequest                   = 400 // Bad Request
	StatusUnauthorized                 = 401 // Unauthorized
	StatusForbidden                    = 403 // Forbidden
	StatusUnknownAttribute             = 420 // Unknown Attribute
	StatusAllocationMismatch           = 437 // Allocation Mismatch
	StatusStaleNonce                   = 438 // Stale Nonce
	StatusAddressFamilynotSupported    = 440 // Address Family not Supported
	StatusWrongCredentials             = 441 // Wrong Credentials
	StatusUnsupportedTransportProtocol = 442 // Unsupported Transport Protocol
	StatusPeerAddressFamilyMismatch    = 443 // Peer Address Family Mismatch
	StatusConnectionAlreadyExists      = 446 // Connection Already Exists
	StatusConnectionTimeoutorFailure   = 447 // Connection Timeout or Failure
	StatusAllocationQuotaReached       = 486 // Allocation Quota Reached
	StatusRoleConflict                 = 487 // Role Conflict
	StatusServerError                  = 500 // Server Error
	StatusInsufficientCapacity         = 508 // Insufficient Capacity
)
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package storage

import (
	"os"
	"syscall"
)

type unixFileLock struct {
	f *os.File
}

func (fl *unixFileLock) release() error {
	if err := setFileLock(fl.f, false); err != nil {
		return err
	}
	return fl.f.Close()
}

func newFileLock(path string) (fl fileLock, err error) {
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return
	}
	err = setFileLock(f, true)
	if err != nil {
		f.Close()
		return
	}
	fl = &unixFileLock{f: f}
	return
}

func setFileLock(f *os.File, lock bool) error {
	flock := syscall.Flock_t{
		Type:   syscall.F_UNLCK,
		Start:  0,
		Len:    0,
		Whence: 1,
	}
	if lock {
		flock.Type = syscall.F_WRLCK
	}
	return syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &flock)
}

func rename(oldpath, newpath string) error {
	return os.Rename(oldpath, newpath)
}

func syncDir(name string) error {
	f, err := os.Open(name)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := f.Sync(); err != nil {
		return err
	}
	return nil
}
<commit_msg>storage: Add build tag for backward compatibility #58<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// +build solaris

package storage

import (
	"os"
	"syscall"
)

type unixFileLock struct {
	f *os.File
}

func (fl *unixFileLock) release() error {
	if err := setFileLock(fl.f, false); err != nil {
		return err
	}
	return fl.f.Close()
}

func newFileLock(path string) (fl fileLock, err error) {
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return
	}
	err = setFileLock(f, true)
	if err != nil {
		f.Close()
		return
	}
	fl = &unixFileLock{f: f}
	return
}

func setFileLock(f *os.File, lock bool) error {
	flock := syscall.Flock_t{
		Type:   syscall.F_UNLCK,
		Start:  0,
		Len:    0,
		Whence: 1,
	}
	if lock {
		flock.Type = syscall.F_WRLCK
	}
	return syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &flock)
}

func rename(oldpath, newpath string) error {
	return os.Rename(oldpath, newpath)
}

func syncDir(name string) error {
	f, err := os.Open(name)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := f.Sync(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package serverrpc

import (
	"bytes"
	"io"

	enginetypes "github.com/docker/engine-api/types"
	"github.com/golang/glog"
	"github.com/hyperhq/hyperd/types"
	"golang.org/x/net/context"
)

// ImageList implements GET /images/get
func (s *ServerRPC) ImageList(ctx context.Context, req *types.ImageListRequest) (*types.ImageListResponse, error) {
	glog.V(3).Infof("ImageList with request %s", req.String())

	images, err := s.daemon.Daemon.Images(req.FilterArgs, req.Filter, req.All)
	if err != nil {
		glog.Errorf("ImageList error: %v", err)
		return nil, err
	}

	result := make([]*types.ImageInfo, 0, len(images))
	for _, image := range images {
		result = append(result, &types.ImageInfo{
			Id:          image.ID,
			ParentID:    image.ParentID,
			RepoTags:    image.RepoTags,
			RepoDigests: image.RepoDigests,
			Created:     image.Created,
			VirtualSize: image.VirtualSize,
			Labels:      image.Labels,
		})
	}

	return &types.ImageListResponse{
		ImageList: result,
	}, nil
}

// ImagePull pulls a image from registry
func (s *ServerRPC) ImagePull(req *types.ImagePullRequest, stream types.PublicAPI_ImagePullServer) error {
	glog.V(3).Infof("ImagePull with request %s", req.String())

	authConfig := &enginetypes.AuthConfig{}
	if req.Auth != nil {
		authConfig = &enginetypes.AuthConfig{
			Username:      req.Auth.Username,
			Password:      req.Auth.Password,
			Auth:          req.Auth.Auth,
			Email:         req.Auth.Email,
			ServerAddress: req.Auth.Serveraddress,
			RegistryToken: req.Auth.Registrytoken,
		}
	}

	buffer := bytes.NewBuffer([]byte{})
	var pullResult error
	var complete = false
	go func() {
		pullResult = s.daemon.CmdImagePull(req.Image, req.Tag, authConfig, nil, buffer)
		complete = true
	}()

	for {
		data := make([]byte, 512)
		n, err := buffer.Read(data)
		if err == io.EOF {
			if complete {
				break
			} else {
				continue
			}
		}

		if err != nil {
			glog.Errorf("Read image pull stream error: %v", err)
			return err
		}

		if err := stream.Send(&types.ImagePullResponse{Data: data[:n]}); err != nil {
			return err
		}
	}

	return pullResult
}

// ImagePush pushes a local image to registry
func (s *ServerRPC) ImagePush(req *types.ImagePushRequest, stream types.PublicAPI_ImagePushServer) error {
	glog.V(3).Infof("ImagePush with request %s", req.String())

	authConfig := &enginetypes.AuthConfig{}
	if req.Auth != nil {
		authConfig = &enginetypes.AuthConfig{
			Username:      req.Auth.Username,
			Password:      req.Auth.Password,
			Auth:          req.Auth.Auth,
			Email:         req.Auth.Email,
			ServerAddress: req.Auth.Serveraddress,
			RegistryToken: req.Auth.Registrytoken,
		}
	}

	buffer := bytes.NewBuffer([]byte{})
	var pushResult error
	var complete = false
	go func() {
		pushResult = s.daemon.CmdImagePush(req.Repo, req.Tag, authConfig, nil, buffer)
		complete = true
	}()

	for {
		data := make([]byte, 512)
		n, err := buffer.Read(data)
		if err == io.EOF {
			if complete {
				break
			} else {
				continue
			}
		}

		if err != nil {
			glog.Errorf("Read image push stream error: %v", err)
			return err
		}

		if err := stream.Send(&types.ImagePushResponse{Data: data[:n]}); err != nil {
			return err
		}
	}

	return pushResult
}

// ImageDelete deletes a image from hyperd
func (s *ServerRPC) ImageRemove(ctx context.Context, req *types.ImageRemoveRequest) (*types.ImageRemoveResponse, error) {
	glog.V(3).Infof("ImageDelete with request %s", req.String())

	resp, err := s.daemon.CmdImageDelete(req.Image, req.Force, req.Prune)
	if err != nil {
		glog.Errorf("DeleteImage failed: %v", err)
		return nil, err
	}

	return &types.ImageRemoveResponse{
		Images: resp,
	}, nil
}
<commit_msg>Fix #374<commit_after>package serverrpc

import (
	"bytes"
	"io"
	"io/ioutil"

	enginetypes "github.com/docker/engine-api/types"
	"github.com/golang/glog"
	"github.com/hyperhq/hyperd/types"
	"golang.org/x/net/context"
)

// ImageList implements GET /images/get
func (s *ServerRPC) ImageList(ctx context.Context, req *types.ImageListRequest) (*types.ImageListResponse, error) {
	glog.V(3).Infof("ImageList with request %s", req.String())

	images, err := s.daemon.Daemon.Images(req.FilterArgs, req.Filter, req.All)
	if err != nil {
		glog.Errorf("ImageList error: %v", err)
		return nil, err
	}

	result := make([]*types.ImageInfo, 0, len(images))
	for _, image := range images {
		result = append(result, &types.ImageInfo{
			Id:          image.ID,
			ParentID:    image.ParentID,
			RepoTags:    image.RepoTags,
			RepoDigests: image.RepoDigests,
			Created:     image.Created,
			VirtualSize: image.VirtualSize,
			Labels:      image.Labels,
		})
	}

	return &types.ImageListResponse{
		ImageList: result,
	}, nil
}

// ImagePull pulls a image from registry
func (s *ServerRPC) ImagePull(req *types.ImagePullRequest, stream types.PublicAPI_ImagePullServer) error {
	glog.V(3).Infof("ImagePull with request %s", req.String())

	authConfig := &enginetypes.AuthConfig{}
	if req.Auth != nil {
		authConfig = &enginetypes.AuthConfig{
			Username:      req.Auth.Username,
			Password:      req.Auth.Password,
			Auth:          req.Auth.Auth,
			Email:         req.Auth.Email,
			ServerAddress: req.Auth.Serveraddress,
			RegistryToken: req.Auth.Registrytoken,
		}
	}

	buffer := bytes.NewBuffer([]byte{})
	var pullResult error
	var complete = false
	go func() {
		pullResult = s.daemon.CmdImagePull(req.Image, req.Tag, authConfig, nil, buffer)
		complete = true
	}()

	for {
		data := make([]byte, 512)
		n, err := buffer.Read(data)
		if err == io.EOF {
			if complete {
				break
			} else {
				continue
			}
		}

		if err != nil {
			glog.Errorf("Read image pull stream error: %v", err)
			return err
		}

		if err := stream.Send(&types.ImagePullResponse{Data: data[:n]}); err != nil {
			return err
		}
	}

	return pullResult
}

// ImagePush pushes a local image to registry
func (s *ServerRPC) ImagePush(req *types.ImagePushRequest, stream types.PublicAPI_ImagePushServer) error {
	glog.V(3).Infof("ImagePush with request %s", req.String())

	authConfig := &enginetypes.AuthConfig{}
	if req.Auth != nil {
		authConfig = &enginetypes.AuthConfig{
			Username:      req.Auth.Username,
			Password:      req.Auth.Password,
			Auth:          req.Auth.Auth,
			Email:         req.Auth.Email,
			ServerAddress: req.Auth.Serveraddress,
			RegistryToken: req.Auth.Registrytoken,
		}
	}

	buffer := bytes.NewBuffer([]byte{})
	var pushResult error
	var complete = false
	go func() {
		pushResult = s.daemon.CmdImagePush(req.Repo, req.Tag, authConfig, nil, buffer)
		complete = true
	}()

	for {
		data, err := ioutil.ReadAll(buffer)
		if err == io.EOF {
			if complete {
				break
			} else {
				continue
			}
		}

		if err != nil {
			glog.Errorf("Read image push stream error: %v", err)
			return err
		}

		if err := stream.Send(&types.ImagePushResponse{Data: data}); err != nil {
			return err
		}
	}

	return pushResult
}

// ImageRemove deletes a image from hyperd
func (s *ServerRPC) ImageRemove(ctx context.Context, req *types.ImageRemoveRequest) (*types.ImageRemoveResponse, error) {
	glog.V(3).Infof("ImageDelete with request %s", req.String())

	resp, err := s.daemon.CmdImageDelete(req.Image, req.Force, req.Prune)
	if err != nil {
		glog.Errorf("DeleteImage failed: %v", err)
		return nil, err
	}

	return &types.ImageRemoveResponse{
		Images: resp,
	}, nil
}
<|endoftext|>


<commit_before>package bootstrap

import (
	"bytes"
	"fmt"
	"testing"
)

func TestRedactorSingle(t *testing.T) {
	var buf bytes.Buffer

	redactor := NewRedactor(&buf, "[REDACTED]", []string{"ipsum"})

	fmt.Fprint(redactor, "Lorem ipsum dolor sit amet")
	redactor.Flush()

	if buf.String() != "Lorem [REDACTED] dolor sit amet" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorMulti(t *testing.T) {
	var buf bytes.Buffer

	redactor := NewRedactor(&buf, "[REDACTED]", []string{"ipsum", "amet"})

	fmt.Fprint(redactor, "Lorem ipsum dolor sit amet")
	redactor.Flush()

	if buf.String() != "Lorem [REDACTED] dolor sit [REDACTED]" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorWriteBoundaries(t *testing.T) {
	var buf bytes.Buffer

	redactor := NewRedactor(&buf, "[REDACTED]", []string{"ipsum"})

	redactor.Write([]byte("Lorem ip"))
	redactor.Write([]byte("sum dolor sit amet"))
	redactor.Flush()

	if buf.String() != "Lorem [REDACTED] dolor sit amet" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorResetMidStream(t *testing.T) {
	var buf bytes.Buffer
	redactor := NewRedactor(&buf, "[REDACTED]", []string{"secret1111"})

	// start writing to the stream (no trailing newline, to be extra tricky)
	redactor.Write([]byte("redact secret1111 but don't redact secret2222 until"))

	// update the redactor with a new secret
	redactor.Flush() // manual flush is necessary before Reset
	redactor.Reset([]string{"secret1111", "secret2222"})

	// finish writing
	redactor.Write([]byte(" after secret2222 is added\n"))
	redactor.Flush()

	if buf.String() != "redact [REDACTED] but don't redact secret2222 until after [REDACTED] is added\n" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}
<commit_msg>Test single byte writes are redacted<commit_after>package bootstrap

import (
	"bytes"
	"fmt"
	"testing"
)

func TestRedactorSingle(t *testing.T) {
	var buf bytes.Buffer

	redactor := NewRedactor(&buf, "[REDACTED]", []string{"ipsum"})

	fmt.Fprint(redactor, "Lorem ipsum dolor sit amet")
	redactor.Flush()

	if buf.String() != "Lorem [REDACTED] dolor sit amet" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorMulti(t *testing.T) {
	var buf bytes.Buffer

	redactor := NewRedactor(&buf, "[REDACTED]", []string{"ipsum", "amet"})

	fmt.Fprint(redactor, "Lorem ipsum dolor sit amet")
	redactor.Flush()

	if buf.String() != "Lorem [REDACTED] dolor sit [REDACTED]" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorWriteBoundaries(t *testing.T) {
	var buf bytes.Buffer

	redactor := NewRedactor(&buf, "[REDACTED]", []string{"ipsum"})

	redactor.Write([]byte("Lorem ip"))
	redactor.Write([]byte("sum dolor sit amet"))
	redactor.Flush()

	if buf.String() != "Lorem [REDACTED] dolor sit amet" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorResetMidStream(t *testing.T) {
	var buf bytes.Buffer
	redactor := NewRedactor(&buf, "[REDACTED]", []string{"secret1111"})

	// start writing to the stream (no trailing newline, to be extra tricky)
	redactor.Write([]byte("redact secret1111 but don't redact secret2222 until"))

	// update the redactor with a new secret
	redactor.Flush() // manual flush is necessary before Reset
	redactor.Reset([]string{"secret1111", "secret2222"})

	// finish writing
	redactor.Write([]byte(" after secret2222 is added\n"))
	redactor.Flush()

	if buf.String() != "redact [REDACTED] but don't redact secret2222 until after [REDACTED] is added\n" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}

func TestRedactorSlowLoris(t *testing.T) {
	var buf bytes.Buffer
	redactor := NewRedactor(&buf, "[REDACTED]", []string{"secret1111"})

	redactor.Write([]byte("s"))
	redactor.Write([]byte("e"))
	redactor.Write([]byte("c"))
	redactor.Write([]byte("r"))
	redactor.Write([]byte("e"))
	redactor.Write([]byte("t"))
	redactor.Write([]byte("1"))
	redactor.Write([]byte("1"))
	redactor.Write([]byte("1"))
	redactor.Write([]byte("1"))
	redactor.Flush()

	if buf.String() != "[REDACTED]" {
		t.Errorf("Redaction failed: %s", buf.String())
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudwatchLogSubscriptionFilter_basic(t *testing.T) {
	var conf lambda.GetFunctionOutput

	rstring := acctest.RandString(5)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudwatchLogSubscriptionFilterDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsCloudwatchLogSubscriptionFilterExists("aws_cloudwatch_log_subscription_filter.test_lambdafunction_logfilter", &conf, rstring),
					testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(&conf, rstring),
				),
			},
		},
	})
}

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

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

		_, err := conn.GetFunction(&lambda.GetFunctionInput{
			FunctionName: aws.String(rs.Primary.ID),
		})

		if err == nil {
			return fmt.Errorf("Lambda Function still exists")
		}

	}

	return nil

}

func testAccCheckAwsCloudwatchLogSubscriptionFilterExists(n string, function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	// Wait for IAM role
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Lambda function not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Lambda function ID not set")
		}

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

		params := &lambda.GetFunctionInput{
			FunctionName: aws.String("example_lambda_name_" + rstring),
		}

		getFunction, err := conn.GetFunction(params)
		if err != nil {
			return err
		}

		*function = *getFunction

		return nil
	}
}

func testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		c := function.Configuration
		expectedName := fmt.Sprintf("example_lambda_name_%s", rstring)
		if *c.FunctionName != expectedName {
			return fmt.Errorf("Expected function name %s, got %s", expectedName, *c.FunctionName)
		}

		if *c.FunctionArn == "" {
			return fmt.Errorf("Could not read Lambda Function's ARN")
		}

		return nil
	}
}

func testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring string) string {
	return fmt.Sprintf(`
resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" {
  name            = "test_lambdafunction_logfilter_%s"
  log_group_name  = "example_lambda_name"
  filter_pattern  = "logtype test"
  destination_arn = "${aws_lambda_function.test_lambdafunction.arn}"
}

resource "aws_lambda_function" "test_lambdafunction" {
  filename      = "test-fixtures/lambdatest.zip"
  function_name = "example_lambda_name_%s"
  role          = "${aws_iam_role.iam_for_lambda.arn}"
  runtime       = "nodejs4.3"
  handler       = "exports.handler"
}

resource "aws_cloudwatch_log_group" "logs" {
  name              = "example_lambda_name_%s"
  retention_in_days = 1
}

resource "aws_lambda_permission" "allow_cloudwatch_logs" {
  statement_id  = "AllowExecutionFromCloudWatchLogs"
  action        = "lambda:*"
  function_name = "${aws_lambda_function.test_lambdafunction.arn}"
  principal     = "logs.us-west-2.amazonaws.com"
}

resource "aws_iam_role" "iam_for_lambda" {
  name = "test_lambdafuntion_iam_role_%s"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test_lambdafunction_iam_policy" {
  name = "test_lambdafunction_iam_policy_%s"
  role = "${aws_iam_role.iam_for_lambda.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1441111030000",
      "Effect": "Allow",
      "Action": [
        "lambda:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}
`, rstring, rstring, rstring, rstring, rstring)
}
<commit_msg>provider/aws: fix TestAccAWSCloudwatchLogSubscriptionFilter_basic by linking the name<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudwatchLogSubscriptionFilter_basic(t *testing.T) {
	var conf lambda.GetFunctionOutput

	rstring := acctest.RandString(5)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudwatchLogSubscriptionFilterDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsCloudwatchLogSubscriptionFilterExists("aws_cloudwatch_log_subscription_filter.test_lambdafunction_logfilter", &conf, rstring),
					testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(&conf, rstring),
				),
			},
		},
	})
}

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

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

		_, err := conn.GetFunction(&lambda.GetFunctionInput{
			FunctionName: aws.String(rs.Primary.ID),
		})

		if err == nil {
			return fmt.Errorf("Lambda Function still exists")
		}

	}

	return nil

}

func testAccCheckAwsCloudwatchLogSubscriptionFilterExists(n string, function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	// Wait for IAM role
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Lambda function not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Lambda function ID not set")
		}

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

		params := &lambda.GetFunctionInput{
			FunctionName: aws.String("example_lambda_name_" + rstring),
		}

		getFunction, err := conn.GetFunction(params)
		if err != nil {
			return err
		}

		*function = *getFunction

		return nil
	}
}

func testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		c := function.Configuration
		expectedName := fmt.Sprintf("example_lambda_name_%s", rstring)
		if *c.FunctionName != expectedName {
			return fmt.Errorf("Expected function name %s, got %s", expectedName, *c.FunctionName)
		}

		if *c.FunctionArn == "" {
			return fmt.Errorf("Could not read Lambda Function's ARN")
		}

		return nil
	}
}

func testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring string) string {
	return fmt.Sprintf(`
resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" {
  name            = "test_lambdafunction_logfilter_%s"
  log_group_name  = "${aws_cloudwatch_log_group.logs.name}"
  filter_pattern  = "logtype test"
  destination_arn = "${aws_lambda_function.test_lambdafunction.arn}"
}

resource "aws_lambda_function" "test_lambdafunction" {
  filename      = "test-fixtures/lambdatest.zip"
  function_name = "example_lambda_name_%s"
  role          = "${aws_iam_role.iam_for_lambda.arn}"
  runtime       = "nodejs4.3"
  handler       = "exports.handler"
}

resource "aws_cloudwatch_log_group" "logs" {
  name              = "example_lambda_name_%s"
  retention_in_days = 1
}

resource "aws_lambda_permission" "allow_cloudwatch_logs" {
  statement_id  = "AllowExecutionFromCloudWatchLogs"
  action        = "lambda:*"
  function_name = "${aws_lambda_function.test_lambdafunction.arn}"
  principal     = "logs.us-west-2.amazonaws.com"
}

resource "aws_iam_role" "iam_for_lambda" {
  name = "test_lambdafuntion_iam_role_%s"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test_lambdafunction_iam_policy" {
  name = "test_lambdafunction_iam_policy_%s"
  role = "${aws_iam_role.iam_for_lambda.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1441111030000",
      "Effect": "Allow",
      "Action": [
        "lambda:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}
`, rstring, rstring, rstring, rstring, rstring)
}
<|endoftext|>


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

package main_test

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"testing"
)

const (
	dataDir = "testdata"
	binary  = "testvet"
)

// Run this shell script, but do it in Go so it can be run by "go test".
// 	go build -o testvet
// 	$(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s
// 	rm testvet
//
func TestVet(t *testing.T) {
	// Plan 9 and Windows systems can't be guaranteed to have Perl and so can't run errchk.
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skip("skipping test; no Perl on %q", runtime.GOOS)
	}

	// go build
	cmd := exec.Command("go", "build", "-o", binary)
	run(cmd, t)

	// defer removal of vet
	defer os.Remove(binary)

	// errchk ./testvet
	gos, err := filepath.Glob(filepath.Join(dataDir, "*.go"))
	if err != nil {
		t.Fatal(err)
	}
	asms, err := filepath.Glob(filepath.Join(dataDir, "*.s"))
	if err != nil {
		t.Fatal(err)
	}
	files := append(gos, asms...)
	errchk := filepath.Join(runtime.GOROOT(), "test", "errchk")
	flags := []string{
		"./" + binary,
		"-printfuncs=Warn:1,Warnf:1",
		"-test", // TODO: Delete once -shadow is part of -all.
	}
	cmd = exec.Command(errchk, append(flags, files...)...)
	if !run(cmd, t) {
		t.Fatal("vet command failed")
	}
}

func run(c *exec.Cmd, t *testing.T) bool {
	output, err := c.CombinedOutput()
	os.Stderr.Write(output)
	if err != nil {
		t.Fatal(err)
	}
	// Errchk delights by not returning non-zero status if it finds errors, so we look at the output.
	// It prints "BUG" if there is a failure.
	if !c.ProcessState.Success() {
		return false
	}
	return !bytes.Contains(output, []byte("BUG"))
}

// TestTags verifies that the -tags argument controls which files to check.
func TestTags(t *testing.T) {
	// go build
	cmd := exec.Command("go", "build", "-o", binary)
	run(cmd, t)

	// defer removal of vet
	defer os.Remove(binary)

	args := []string{
		"-tags=testtag",
		"-v", // We're going to look at the files it examines.
		"testdata/tagtest",
	}
	cmd = exec.Command("./"+binary, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatal(err)
	}
	// file1 has testtag and file2 has !testtag.
	if !bytes.Contains(output, []byte("tagtest/file1.go")) {
		t.Error("file1 was excluded, should be included")
	}
	if bytes.Contains(output, []byte("tagtest/file2.go")) {
		t.Error("file2 was included, should be excluded")
	}
}
<commit_msg>cmd/vet: skip exec tests on systems that can't exec<commit_after>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main_test

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

const (
	dataDir = "testdata"
	binary  = "testvet"
)

// Run this shell script, but do it in Go so it can be run by "go test".
// 	go build -o testvet
// 	$(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s
// 	rm testvet
//
func TestVet(t *testing.T) {
	switch runtime.GOOS {
	case "plan9", "windows":
		// Plan 9 and Windows systems can't be guaranteed to have Perl and so can't run errchk.
		t.Skip("skipping test; no Perl on %q", runtime.GOOS)
	case "nacl":
		t.Skip("skipping test; no command execution on nacl")
	case "darwin":
		if strings.HasPrefix(runtime.GOARCH, "arm") {
			t.Skip("skipping test; no command execution on darwin/%s", runtime.GOARCH)
		}
	}

	// go build
	cmd := exec.Command("go", "build", "-o", binary)
	run(cmd, t)

	// defer removal of vet
	defer os.Remove(binary)

	// errchk ./testvet
	gos, err := filepath.Glob(filepath.Join(dataDir, "*.go"))
	if err != nil {
		t.Fatal(err)
	}
	asms, err := filepath.Glob(filepath.Join(dataDir, "*.s"))
	if err != nil {
		t.Fatal(err)
	}
	files := append(gos, asms...)
	errchk := filepath.Join(runtime.GOROOT(), "test", "errchk")
	flags := []string{
		"./" + binary,
		"-printfuncs=Warn:1,Warnf:1",
		"-test", // TODO: Delete once -shadow is part of -all.
	}
	cmd = exec.Command(errchk, append(flags, files...)...)
	if !run(cmd, t) {
		t.Fatal("vet command failed")
	}
}

func run(c *exec.Cmd, t *testing.T) bool {
	output, err := c.CombinedOutput()
	os.Stderr.Write(output)
	if err != nil {
		t.Fatal(err)
	}
	// Errchk delights by not returning non-zero status if it finds errors, so we look at the output.
	// It prints "BUG" if there is a failure.
	if !c.ProcessState.Success() {
		return false
	}
	return !bytes.Contains(output, []byte("BUG"))
}

// TestTags verifies that the -tags argument controls which files to check.
func TestTags(t *testing.T) {
	switch runtime.GOOS {
	case "nacl":
		t.Skip("skipping test; no command execution on nacl")
	case "darwin":
		if strings.HasPrefix(runtime.GOARCH, "arm") {
			t.Skip("skipping test; no command execution on darwin/%s", runtime.GOARCH)
		}
	}

	// go build
	cmd := exec.Command("go", "build", "-o", binary)
	run(cmd, t)

	// defer removal of vet
	defer os.Remove(binary)

	args := []string{
		"-tags=testtag",
		"-v", // We're going to look at the files it examines.
		"testdata/tagtest",
	}
	cmd = exec.Command("./"+binary, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatal(err)
	}
	// file1 has testtag and file2 has !testtag.
	if !bytes.Contains(output, []byte("tagtest/file1.go")) {
		t.Error("file1 was excluded, should be included")
	}
	if bytes.Contains(output, []byte("tagtest/file2.go")) {
		t.Error("file2 was included, should be excluded")
	}
}
<|endoftext|>


<commit_before>package expr

import (
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/errors"
	"github.com/grafana/metrictank/util"
	log "github.com/sirupsen/logrus"
)

var (
	ErrMissingArg          = errors.NewBadRequest("argument missing")
	ErrTooManyArg          = errors.NewBadRequest("too many arguments")
	ErrMissingTimeseries   = errors.NewBadRequest("missing time series argument")
	ErrWildcardNotAllowed  = errors.NewBadRequest("found wildcard where series expected")
	ErrMissingExpr         = errors.NewBadRequest("missing expression")
	ErrMissingComma        = errors.NewBadRequest("missing comma")
	ErrMissingQuote        = errors.NewBadRequest("missing quote")
	ErrUnexpectedCharacter = errors.NewBadRequest("unexpected character")
	ErrIllegalCharacter    = errors.NewBadRequest("illegal character for function name")
)

type ErrBadArgument struct {
	exp reflect.Type
	got reflect.Type
}

func (e ErrBadArgument) Error() string {
	return fmt.Sprintf("argument bad type. expected %s - got %s", e.exp, e.got)
}

func (e ErrBadArgument) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrBadArgumentStr struct {
	exp string
	got string
}

func (e ErrBadArgumentStr) Error() string {
	return fmt.Sprintf("argument bad type. expected %s - got %s", e.exp, e.got)
}

func (e ErrBadArgumentStr) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrBadRegex struct {
	error
}

func (e ErrBadRegex) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrUnknownFunction string

func (e ErrUnknownFunction) Error() string {
	return fmt.Sprintf("unknown function %q", string(e))
}

func (e ErrUnknownFunction) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrUnknownKwarg struct {
	key string
}

func (e ErrUnknownKwarg) Error() string {
	return fmt.Sprintf("unknown keyword argument %q", e.key)
}

func (e ErrUnknownKwarg) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrBadKwarg struct {
	key string
	exp Arg
	got exprType
}

func (e ErrBadKwarg) Error() string {
	return fmt.Sprintf("keyword argument %q bad type. expected %T - got %s", e.key, e.exp, e.got)
}

func (e ErrBadKwarg) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrKwargSpecifiedTwice struct {
	key string
}

func (e ErrKwargSpecifiedTwice) Error() string {
	return fmt.Sprintf("keyword argument %q specified twice", e.key)
}

func (e ErrKwargSpecifiedTwice) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type MetricRequest struct {
	Metric string
	From   int32
	Until  int32
}

// ParseMany parses a slice of strings into a slice of expressions (recursively)
// not included: validation that requested functions exist, correct args are passed, etc.
func ParseMany(targets []string) ([]*expr, error) {
	var out []*expr
	for _, target := range targets {
		e, leftover, err := Parse(target)
		if err != nil {
			return nil, err
		}
		if leftover != "" {
			return nil, errors.NewBadRequestf("failed to parse %q fully. got leftover %q", target, leftover)
		}
		out = append(out, e)
	}
	return out, nil
}

// Parses an expression string and turns it into an expression
// also returns any leftover data that could not be parsed
func Parse(e string) (*expr, string, error) {
	// skip whitespace
	for len(e) > 1 && e[0] == ' ' {
		e = e[1:]
	}

	if len(e) == 0 {
		return nil, "", ErrMissingExpr
	}

	if '0' <= e[0] && e[0] <= '9' || e[0] == '-' || e[0] == '+' {
		return parseConst(e)
	}

	if val, ok := strToBool(e); ok {
		// 'false' is 5 chars, 'true' is 4
		size := 5
		if val {
			size = 4
		}
		return &expr{bool: val, str: e[:size], etype: etBool}, e[size:], nil
	}

	if e[0] == '\'' || e[0] == '"' {
		val, e, err := parseString(e)
		return &expr{str: val, etype: etString}, e, err
	}

	name, e := parseName(e)

	if name == "" {
		return nil, e, ErrMissingArg
	}

	if e != "" && e[0] == '(' {
		for i := range name {
			if !isFnChar(name[i]) {
				return nil, "", ErrIllegalCharacter
			}
		}

		exp := &expr{str: name, etype: etFunc}

		ArgString, posArgs, namedArgs, e, err := parseArgList(e)
		exp.argsStr = ArgString
		exp.args = posArgs
		exp.namedArgs = namedArgs

		return exp, e, err
	}

	return &expr{str: name, etype: etName}, e, nil
}

func strToBool(val string) (bool, bool) {
	if strings.HasPrefix(val, "True") || strings.HasPrefix(val, "true") {
		return true, true
	}

	if strings.HasPrefix(val, "False") || strings.HasPrefix(val, "false") {
		return false, true
	}

	return false, false
}

func strToInt(val string) (int, bool) {
	i, err := strconv.Atoi(val)
	if err != nil {
		return 0, false
	}
	return i, true
}

func strToFloat(val string) (float64, bool) {
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return 0, false
	}
	return f, true
}

// caller must assure s starts with opening paren
func parseArgList(e string) (string, []*expr, map[string]*expr, string, error) {

	var (
		posArgs   []*expr
		namedArgs map[string]*expr
	)

	if e[0] != '(' {
		panic("arg list should start with paren. calling code should have asserted this")
	}

	ArgString := e[1:]

	e = e[1:]

	for {
		var arg *expr
		var err error
		arg, e, err = Parse(e)
		if err != nil {
			return "", nil, nil, e, err
		}

		if e == "" {
			return "", nil, nil, "", ErrMissingComma
		}

		// we now know we're parsing a key-value pair
		// in the future we should probably add validation here that the key
		// can't contain otherwise-valid-name chars like {, }, etc
		if arg.etype == etName && e[0] == '=' {
			e = e[1:]
			argCont, eCont, errCont := Parse(e)
			if errCont != nil {
				return "", nil, nil, eCont, errCont
			}

			if eCont == "" {
				return "", nil, nil, "", ErrMissingComma
			}

			if argCont.etype != etInt && argCont.etype != etFloat && argCont.etype != etName && argCont.etype != etString && argCont.etype != etBool {
				return "", nil, nil, eCont, ErrBadArgumentStr{"int, float, name, bool or string", string(argCont.etype)}
			}

			if namedArgs == nil {
				namedArgs = make(map[string]*expr)
			}

			namedArgs[arg.str] = argCont

			e = eCont
		} else {
			posArgs = append(posArgs, arg)
		}

		// after the argument, trim any trailing spaces
		for len(e) > 0 && e[0] == ' ' {
			e = e[1:]
		}

		if e[0] == ')' {
			return ArgString[:len(ArgString)-len(e)], posArgs, namedArgs, e[1:], nil
		}

		if e[0] != ',' && e[0] != ' ' {
			return "", nil, nil, "", ErrUnexpectedCharacter
		}

		e = e[1:]
	}
}

var nameChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&*+-/:;<>?@[]^_|~."

func isNameChar(r byte) bool {
	return strings.IndexByte(nameChar, r) >= 0
}

func isFnChar(r byte) bool {
	return false ||
		'a' <= r && r <= 'z' ||
		'A' <= r && r <= 'Z' ||
		'0' <= r && r <= '9'
}

func parseConst(s string) (*expr, string, error) {

	var i int
	var float bool
	// All valid characters for a floating-point constant
	// Just slurp them all in and let ParseFloat sort 'em out
	for i < len(s) && (util.IsDigit(s[i]) || s[i] == '.' || s[i] == '+' || s[i] == '-' || s[i] == 'e' || s[i] == 'E') {
		// note that exponent syntax results into a float value.
		// so even values like 1e3 (1000) or 2000e-3 (2) which can be expressed as integers,
		// are considered floating point values.  if a function expets an int, then just don't use 'e' syntax.
		if s[i] == '.' || s[i] == 'e' || s[i] == 'E' {
			float = true
		}
		i++
	}

	if float {
		v, err := strconv.ParseFloat(s[:i], 64)
		return &expr{float: v, str: s[:i], etype: etFloat}, s[i:], err
	}
	v, err := strconv.ParseInt(s[:i], 10, 64)
	return &expr{int: v, str: s[:i], etype: etInt}, s[i:], err
}

// parseName parses a "name" which is either:
// 1) a metric expression
// 2) the key of a keyword argument
// 3) a function name
// It's up to the caller to determine which, based on context. E.g.:
// * if leftover data starts with '(' then assume 3 and validate function name
// * if leftover data starts with '=' assume 2
// * fallback to assuming 1.
// it returns the parsed name and any leftover data
func parseName(s string) (string, string) {

	var i int
	allowEqual := false

FOR:
	for braces := 0; i < len(s); i++ {
		// if the current expression is a metric name with ";" (59) we should
		// allow the "=" (61) character to be part of the metric name
		if isNameChar(s[i]) || (allowEqual && s[i] == 61) {
			if s[i] == 59 {
				allowEqual = true
			}
			continue
		}

		switch s[i] {
		case '{':
			braces++
		case '}':
			if braces == 0 {
				break FOR

			}
			braces--
		case ',':
			if braces == 0 {
				break FOR
			}
		default:
			break FOR
		}
	}

	if i == len(s) {
		return s, ""
	}

	return s[:i], s[i:]
}

// caller must assure s starts with a single or double quote
func parseString(s string) (string, string, error) {

	if s[0] != '\'' && s[0] != '"' {
		panic("string should start with open quote. calling code should have asserted this")
	}

	match := s[0]

	s = s[1:]

	var i int
	for i < len(s) && s[i] != match {
		i++
	}

	if i == len(s) {
		return "", "", ErrMissingQuote

	}

	return s[:i], s[i+1:], nil
}

// extractMetric searches for a metric name or path Expression in `m'
// metric name / path expression is defined by the following criteria:
// 1. Not a function name
// 2. Consists only of name characters
// 	2.1 '=' / ' ' are conditionally allowed if ';' is found (denoting tag format)
// 	2.2 ',' is conditionally allowed within matching '{}'
// 3. Is not a string literal (i.e. contained within single/double quote pairs)
func extractMetric(m string) string {
	start := 0
	end := 0
	curlyBraces := 0
	quoteChar := byte(0)
	allowTagChars := false
	for end < len(m) {
		c := m[end]
		if (c == '\'' || c == '"') && (end == 0 || m[end-1] != '\\') {
			// Found a non-escaped quote char
			if quoteChar == 0 {
				quoteChar = c
				start = end + 1
			} else if c == quoteChar {
				quoteChar = byte(0)
				start = end + 1
			}
		} else if quoteChar == 0 {
			if c == '{' {
				curlyBraces++
			} else if c == '}' {
				curlyBraces--
			} else if c == ')' || (c == ',' && curlyBraces == 0) {
				return m[start:end]
			} else if !(isNameChar(c) || c == ',' || (allowTagChars && strings.IndexByte("= ", c) >= 0)) {
				start = end + 1
				allowTagChars = false
			} else if c == ';' {
				allowTagChars = true
			}
		}

		end++
	}

	if quoteChar != 0 {
		log.Warnf("extractMetric: encountered unterminated string literal in %s", m)
		return ""
	}

	return m[start:end]
}

// aggKey returns a string key by applying the selectors
// (integers for node positions or strings for tag names) to the given serie
func aggKey(serie models.Series, nodes []expr) string {
	metric := extractMetric(serie.Target)
	if len(metric) == 0 {
		metric = serie.Tags["name"]
	}
	// Trim off tags (if they are there) and split on '.'
	parts := strings.Split(strings.SplitN(metric, ";", 2)[0], ".")
	var name []string
	for _, n := range nodes {
		if n.etype == etInt {
			idx := int(n.int)
			if idx < 0 {
				idx += len(parts)
			}
			if idx >= len(parts) || idx < 0 {
				continue
			}
			name = append(name, parts[idx])
		} else if n.etype == etString {
			s := n.str
			name = append(name, serie.Tags[s])
		}
	}
	return strings.Join(name, ".")
}
<commit_msg>better comment<commit_after>package expr

import (
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/errors"
	"github.com/grafana/metrictank/util"
	log "github.com/sirupsen/logrus"
)

var (
	ErrMissingArg          = errors.NewBadRequest("argument missing")
	ErrTooManyArg          = errors.NewBadRequest("too many arguments")
	ErrMissingTimeseries   = errors.NewBadRequest("missing time series argument")
	ErrWildcardNotAllowed  = errors.NewBadRequest("found wildcard where series expected")
	ErrMissingExpr         = errors.NewBadRequest("missing expression")
	ErrMissingComma        = errors.NewBadRequest("missing comma")
	ErrMissingQuote        = errors.NewBadRequest("missing quote")
	ErrUnexpectedCharacter = errors.NewBadRequest("unexpected character")
	ErrIllegalCharacter    = errors.NewBadRequest("illegal character for function name")
)

type ErrBadArgument struct {
	exp reflect.Type
	got reflect.Type
}

func (e ErrBadArgument) Error() string {
	return fmt.Sprintf("argument bad type. expected %s - got %s", e.exp, e.got)
}

func (e ErrBadArgument) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrBadArgumentStr struct {
	exp string
	got string
}

func (e ErrBadArgumentStr) Error() string {
	return fmt.Sprintf("argument bad type. expected %s - got %s", e.exp, e.got)
}

func (e ErrBadArgumentStr) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrBadRegex struct {
	error
}

func (e ErrBadRegex) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrUnknownFunction string

func (e ErrUnknownFunction) Error() string {
	return fmt.Sprintf("unknown function %q", string(e))
}

func (e ErrUnknownFunction) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrUnknownKwarg struct {
	key string
}

func (e ErrUnknownKwarg) Error() string {
	return fmt.Sprintf("unknown keyword argument %q", e.key)
}

func (e ErrUnknownKwarg) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrBadKwarg struct {
	key string
	exp Arg
	got exprType
}

func (e ErrBadKwarg) Error() string {
	return fmt.Sprintf("keyword argument %q bad type. expected %T - got %s", e.key, e.exp, e.got)
}

func (e ErrBadKwarg) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type ErrKwargSpecifiedTwice struct {
	key string
}

func (e ErrKwargSpecifiedTwice) Error() string {
	return fmt.Sprintf("keyword argument %q specified twice", e.key)
}

func (e ErrKwargSpecifiedTwice) HTTPStatusCode() int {
	return http.StatusBadRequest
}

type MetricRequest struct {
	Metric string
	From   int32
	Until  int32
}

// ParseMany parses a slice of strings into a slice of expressions (recursively)
// not included: validation that requested functions exist, correct args are passed, etc.
func ParseMany(targets []string) ([]*expr, error) {
	var out []*expr
	for _, target := range targets {
		e, leftover, err := Parse(target)
		if err != nil {
			return nil, err
		}
		if leftover != "" {
			return nil, errors.NewBadRequestf("failed to parse %q fully. got leftover %q", target, leftover)
		}
		out = append(out, e)
	}
	return out, nil
}

// Parses an expression string and turns it into an expression
// also returns any leftover data that could not be parsed
func Parse(e string) (*expr, string, error) {
	// skip whitespace
	for len(e) > 1 && e[0] == ' ' {
		e = e[1:]
	}

	if len(e) == 0 {
		return nil, "", ErrMissingExpr
	}

	if '0' <= e[0] && e[0] <= '9' || e[0] == '-' || e[0] == '+' {
		return parseConst(e)
	}

	if val, ok := strToBool(e); ok {
		// 'false' is 5 chars, 'true' is 4
		size := 5
		if val {
			size = 4
		}
		return &expr{bool: val, str: e[:size], etype: etBool}, e[size:], nil
	}

	if e[0] == '\'' || e[0] == '"' {
		val, e, err := parseString(e)
		return &expr{str: val, etype: etString}, e, err
	}

	name, e := parseName(e)

	if name == "" {
		return nil, e, ErrMissingArg
	}

	if e != "" && e[0] == '(' {
		for i := range name {
			if !isFnChar(name[i]) {
				return nil, "", ErrIllegalCharacter
			}
		}

		exp := &expr{str: name, etype: etFunc}

		ArgString, posArgs, namedArgs, e, err := parseArgList(e)
		exp.argsStr = ArgString
		exp.args = posArgs
		exp.namedArgs = namedArgs

		return exp, e, err
	}

	return &expr{str: name, etype: etName}, e, nil
}

func strToBool(val string) (bool, bool) {
	if strings.HasPrefix(val, "True") || strings.HasPrefix(val, "true") {
		return true, true
	}

	if strings.HasPrefix(val, "False") || strings.HasPrefix(val, "false") {
		return false, true
	}

	return false, false
}

func strToInt(val string) (int, bool) {
	i, err := strconv.Atoi(val)
	if err != nil {
		return 0, false
	}
	return i, true
}

func strToFloat(val string) (float64, bool) {
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return 0, false
	}
	return f, true
}

// caller must assure s starts with opening paren
func parseArgList(e string) (string, []*expr, map[string]*expr, string, error) {

	var (
		posArgs   []*expr
		namedArgs map[string]*expr
	)

	if e[0] != '(' {
		panic("arg list should start with paren. calling code should have asserted this")
	}

	ArgString := e[1:]

	e = e[1:]

	for {
		var arg *expr
		var err error
		arg, e, err = Parse(e)
		if err != nil {
			return "", nil, nil, e, err
		}

		if e == "" {
			return "", nil, nil, "", ErrMissingComma
		}

		// we now know we're parsing a key-value pair
		// in the future we should probably add validation here that the key
		// can't contain otherwise-valid-name chars like {, }, etc
		if arg.etype == etName && e[0] == '=' {
			e = e[1:]
			argCont, eCont, errCont := Parse(e)
			if errCont != nil {
				return "", nil, nil, eCont, errCont
			}

			if eCont == "" {
				return "", nil, nil, "", ErrMissingComma
			}

			if argCont.etype != etInt && argCont.etype != etFloat && argCont.etype != etName && argCont.etype != etString && argCont.etype != etBool {
				return "", nil, nil, eCont, ErrBadArgumentStr{"int, float, name, bool or string", string(argCont.etype)}
			}

			if namedArgs == nil {
				namedArgs = make(map[string]*expr)
			}

			namedArgs[arg.str] = argCont

			e = eCont
		} else {
			posArgs = append(posArgs, arg)
		}

		// after the argument, trim any trailing spaces
		for len(e) > 0 && e[0] == ' ' {
			e = e[1:]
		}

		if e[0] == ')' {
			return ArgString[:len(ArgString)-len(e)], posArgs, namedArgs, e[1:], nil
		}

		if e[0] != ',' && e[0] != ' ' {
			return "", nil, nil, "", ErrUnexpectedCharacter
		}

		e = e[1:]
	}
}

var nameChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&*+-/:;<>?@[]^_|~."

func isNameChar(r byte) bool {
	return strings.IndexByte(nameChar, r) >= 0
}

func isFnChar(r byte) bool {
	return false ||
		'a' <= r && r <= 'z' ||
		'A' <= r && r <= 'Z' ||
		'0' <= r && r <= '9'
}

func parseConst(s string) (*expr, string, error) {

	var i int
	var float bool
	// All valid characters for a floating-point constant
	// Just slurp them all in and let ParseFloat sort 'em out
	for i < len(s) && (util.IsDigit(s[i]) || s[i] == '.' || s[i] == '+' || s[i] == '-' || s[i] == 'e' || s[i] == 'E') {
		// note that exponent syntax results into a float value.
		// so even values like 1e3 (1000) or 2000e-3 (2) which can be expressed as integers,
		// are considered floating point values.  if a function expets an int, then just don't use 'e' syntax.
		if s[i] == '.' || s[i] == 'e' || s[i] == 'E' {
			float = true
		}
		i++
	}

	if float {
		v, err := strconv.ParseFloat(s[:i], 64)
		return &expr{float: v, str: s[:i], etype: etFloat}, s[i:], err
	}
	v, err := strconv.ParseInt(s[:i], 10, 64)
	return &expr{int: v, str: s[:i], etype: etInt}, s[i:], err
}

// parseName parses a "name" which is either:
// 1) a metric expression
// 2) the key of a keyword argument
// 3) a function name
// It's up to the caller to determine which, based on context. E.g.:
// * if leftover data starts with '(' then assume 3 and validate function name
// * if leftover data starts with '=' assume 2
// * fallback to assuming 1.
// it returns the parsed name and any leftover data
func parseName(s string) (string, string) {

	var i int
	allowEqual := false

FOR:
	for braces := 0; i < len(s); i++ {
		// if the current expression is a metric name with ";" (59) we should
		// allow the "=" (61) character to be part of the metric name
		if isNameChar(s[i]) || (allowEqual && s[i] == 61) {
			if s[i] == 59 {
				allowEqual = true
			}
			continue
		}

		switch s[i] {
		case '{':
			braces++
		case '}':
			if braces == 0 {
				break FOR

			}
			braces--
		case ',':
			if braces == 0 {
				break FOR
			}
		default:
			break FOR
		}
	}

	if i == len(s) {
		return s, ""
	}

	return s[:i], s[i:]
}

// caller must assure s starts with a single or double quote
func parseString(s string) (string, string, error) {

	if s[0] != '\'' && s[0] != '"' {
		panic("string should start with open quote. calling code should have asserted this")
	}

	match := s[0]

	s = s[1:]

	var i int
	for i < len(s) && s[i] != match {
		i++
	}

	if i == len(s) {
		return "", "", ErrMissingQuote

	}

	return s[:i], s[i+1:], nil
}

// extractMetric searches for a metric name or path Expression in `m'
// metric name / path expression is defined by the following criteria:
// 1. Not a function name
// 2. Consists only of name characters
// 	2.1 '=' and ' ' are conditionally allowed if ';' is found (denoting tag format)
// 	2.2 ',' is conditionally allowed within matching '{}'
// 3. Is not a string literal (i.e. contained within single/double quote pairs)
func extractMetric(m string) string {
	start := 0
	end := 0
	curlyBraces := 0
	quoteChar := byte(0)
	allowTagChars := false
	for end < len(m) {
		c := m[end]
		if (c == '\'' || c == '"') && (end == 0 || m[end-1] != '\\') {
			// Found a non-escaped quote char
			if quoteChar == 0 {
				quoteChar = c
				start = end + 1
			} else if c == quoteChar {
				quoteChar = byte(0)
				start = end + 1
			}
		} else if quoteChar == 0 {
			if c == '{' {
				curlyBraces++
			} else if c == '}' {
				curlyBraces--
			} else if c == ')' || (c == ',' && curlyBraces == 0) {
				return m[start:end]
			} else if !(isNameChar(c) || c == ',' || (allowTagChars && strings.IndexByte("= ", c) >= 0)) {
				start = end + 1
				allowTagChars = false
			} else if c == ';' {
				allowTagChars = true
			}
		}

		end++
	}

	if quoteChar != 0 {
		log.Warnf("extractMetric: encountered unterminated string literal in %s", m)
		return ""
	}

	return m[start:end]
}

// aggKey returns a string key by applying the selectors
// (integers for node positions or strings for tag names) to the given serie
func aggKey(serie models.Series, nodes []expr) string {
	metric := extractMetric(serie.Target)
	if len(metric) == 0 {
		metric = serie.Tags["name"]
	}
	// Trim off tags (if they are there) and split on '.'
	parts := strings.Split(strings.SplitN(metric, ";", 2)[0], ".")
	var name []string
	for _, n := range nodes {
		if n.etype == etInt {
			idx := int(n.int)
			if idx < 0 {
				idx += len(parts)
			}
			if idx >= len(parts) || idx < 0 {
				continue
			}
			name = append(name, parts[idx])
		} else if n.etype == etString {
			s := n.str
			name = append(name, serie.Tags[s])
		}
	}
	return strings.Join(name, ".")
}
<|endoftext|>


<commit_before>package prium

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/golang/glog"
	"github.com/pkg/errors"
	"io"
	"os"
	"path"
	"strings"
)

// S3 ...
type S3 struct {
	config   *Config
	agent    *Agent
	svc      *s3.S3
	uploader *s3manager.Uploader
}

// NewS3 ..
func NewS3(config *Config, agent *Agent) *S3 {
	return &S3{
		config: config,
		agent:  agent,
	}
}

// Init S3 object.
func (s *S3) Init() error {
	// create new session
	sess := session.New(&aws.Config{
		Region:      aws.String(s.config.AwsRegion),
		Credentials: credentials.NewStaticCredentials(s.config.AwsAccessKey, s.config.AwsSecretKey, ""),
	})
	s.svc = s3.New(sess)
	s.uploader = s3manager.NewUploader(sess)
	s.agent = NewAgent(s.config)
	return nil
}

// UploadFiles ...
// TODO: retry upload if initial upload fails.
func (s *S3) UploadFiles(env, keyspace, parent, timestamp, host string, files []string) error {
	glog.Infof("uploading files to s3...")
	for _, file := range files {
		key := getFileKey(env, keyspace, parent, timestamp, host, file, s.config.Incremental)
		glog.Infof("upload key: %s", key)

		// read bytes from file@host
		b, err := s.agent.ReadFile(host, file)
		if err != nil {
			glog.Errorf("error reading file %s on host %s", file, host)
			return err
		}

		// gzip files before uploading
		reader, writer := io.Pipe()
		go func() {
			gw := gzip.NewWriter(writer)
			r := bytes.NewReader(b)
			io.Copy(gw, r)
			gw.Close()
			writer.Close()
		}()

		// details of file to upload
		params := &s3manager.UploadInput{
			Bucket: aws.String(s.config.AwsBucket),
			Body:   reader,
			Key:    aws.String(key),
		}

		// upload file
		_, err = s.uploader.Upload(params)
		if err != nil {
			glog.Errorf("error uploading file to S3 :: %v", err)
			//return errors.Wrap(err, "error uploading file to S3")
		}
	}
	return nil
}

// getFileKey creates a unique key for backup file that would be uploaded
// to AWS S3.
func getFileKey(environment, keyspace, parent, timestamp, host, file string, incremental bool) string {
	dir, base := path.Split(path.Clean(file))
	dir, _ = path.Split(path.Clean(dir))
	if !incremental {
		dir, _ = path.Split(path.Clean(dir))
	}
	return fmt.Sprintf("/%s/%s/%s/%s/%s%s%s.gz",
		environment, keyspace, parent, timestamp, host, dir, base)
}

// downloadKeys downloads a list of keys from S3 to local machine.
func (s *S3) downloadKeys(keys []string, prefix string) (map[string]string, error) {
	glog.Infof("downloading %d keys", len(keys))
	files := make(map[string]string)
	for _, key := range keys {
		file, err := s.downloadKey(key, prefix)
		if err != nil {
			return nil, errors.Wrap(err, "error downloading key")
		}
		files[key] = file
	}
	return files, nil
}

func (s *S3) downloadKey(key, prefix string) (string, error) {
	glog.V(2).Infof("download key: %s", key)
	fileName := strings.TrimSuffix(fmt.Sprintf("%s/%s", prefix, key), ".gz")
	params := &s3.GetObjectInput{
		Bucket: aws.String(s.config.AwsBucket),
		Key:    aws.String(key),
	}
	resp, err := s.svc.GetObject(params)
	if err != nil {
		return "", errors.Wrap(err, fmt.Sprintf("error downloading key: %s", key))
	}

	dir := path.Dir(fileName)
	err = os.MkdirAll(dir, os.ModeDir|os.ModePerm)
	if err != nil {
		return "", errors.Wrap(err, fmt.Sprintf("error creading dir %s", dir))
	}

	file, err := os.Create(fileName)
	if err != nil {
		return "", errors.Wrap(err, "error opening file")
	}

	reader, writer := io.Pipe()
	go func() {
		gr, err := gzip.NewReader(resp.Body)
		if err != nil {
			glog.Errorf("error creating new gzip reader")
			os.Exit(1)
		}
		io.Copy(writer, gr)
		gr.Close()
		writer.Close()
	}()

	var buf []byte
	buf = make([]byte, 1024)
	for {
		n, err := reader.Read(buf)
		if err != nil && err != io.EOF {
			return "", errors.Wrap(err, "error reading file")
		}
		if n == 0 {
			break
		}
		if _, writeErr := file.Write(buf[:n]); writeErr != nil {
			return "", errors.Wrap(err, "error writing file")
		}
		if err == io.EOF {
			break
		}
	}

	if err = reader.Close(); err != nil {
		return "", errors.Wrap(err, "error reading downloaded file")
	}
	if err = file.Close(); err != nil {
		return "", errors.Wrap(err, "error closing file")
	}
	return fileName, nil
}

// SnapshotHistory retrieves snapshot history from S3.
func (s *S3) SnapshotHistory() (*SnapshotHistory, error) {
	prefix := fmt.Sprintf("%s/%s", s.config.AwsBasePath, s.config.Keyspace)
	params := &s3.ListObjectsV2Input{
		Bucket: aws.String(s.config.AwsBucket),
		Prefix: aws.String(prefix),
	}
	resp, err := s.svc.ListObjectsV2(params)
	if err != nil {
		return nil, errors.Wrap(err, "error listing from S3")
	}
	h := NewSnapshotHistory()
	for _, obj := range resp.Contents {
		h.Add(*obj.Key)
	}
	return h, nil
}
<commit_msg>Fix incomplete snapshot history.<commit_after>package prium

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/golang/glog"
	"github.com/pkg/errors"
	"io"
	"os"
	"path"
	"strings"
)

// S3 ...
type S3 struct {
	config   *Config
	agent    *Agent
	svc      *s3.S3
	uploader *s3manager.Uploader
}

// NewS3 ..
func NewS3(config *Config, agent *Agent) *S3 {
	return &S3{
		config: config,
		agent:  agent,
	}
}

// Init S3 object.
func (s *S3) Init() error {
	// create new session
	sess := session.New(&aws.Config{
		Region:      aws.String(s.config.AwsRegion),
		Credentials: credentials.NewStaticCredentials(s.config.AwsAccessKey, s.config.AwsSecretKey, ""),
	})
	s.svc = s3.New(sess)
	s.uploader = s3manager.NewUploader(sess)
	s.agent = NewAgent(s.config)
	return nil
}

// UploadFiles ...
// TODO: retry upload if initial upload fails.
func (s *S3) UploadFiles(env, keyspace, parent, timestamp, host string, files []string) error {
	glog.Infof("uploading files to s3...")
	for _, file := range files {
		key := getFileKey(env, keyspace, parent, timestamp, host, file, s.config.Incremental)
		glog.Infof("upload key: %s", key)

		// read bytes from file@host
		b, err := s.agent.ReadFile(host, file)
		if err != nil {
			glog.Errorf("error reading file %s on host %s", file, host)
			return err
		}

		// gzip files before uploading
		reader, writer := io.Pipe()
		go func() {
			gw := gzip.NewWriter(writer)
			r := bytes.NewReader(b)
			io.Copy(gw, r)
			gw.Close()
			writer.Close()
		}()

		// details of file to upload
		params := &s3manager.UploadInput{
			Bucket: aws.String(s.config.AwsBucket),
			Body:   reader,
			Key:    aws.String(key),
		}

		// upload file
		_, err = s.uploader.Upload(params)
		if err != nil {
			glog.Errorf("error uploading file to S3 :: %v", err)
			//return errors.Wrap(err, "error uploading file to S3")
		}
	}
	return nil
}

// getFileKey creates a unique key for backup file that would be uploaded
// to AWS S3.
func getFileKey(environment, keyspace, parent, timestamp, host, file string, incremental bool) string {
	dir, base := path.Split(path.Clean(file))
	dir, _ = path.Split(path.Clean(dir))
	if !incremental {
		dir, _ = path.Split(path.Clean(dir))
	}
	return fmt.Sprintf("/%s/%s/%s/%s/%s%s%s.gz",
		environment, keyspace, parent, timestamp, host, dir, base)
}

// downloadKeys downloads a list of keys from S3 to local machine.
func (s *S3) downloadKeys(keys []string, prefix string) (map[string]string, error) {
	glog.Infof("downloading %d keys", len(keys))
	files := make(map[string]string)
	for _, key := range keys {
		file, err := s.downloadKey(key, prefix)
		if err != nil {
			return nil, errors.Wrap(err, "error downloading key")
		}
		files[key] = file
	}
	return files, nil
}

func (s *S3) downloadKey(key, prefix string) (string, error) {
	glog.V(2).Infof("download key: %s", key)
	fileName := strings.TrimSuffix(fmt.Sprintf("%s/%s", prefix, key), ".gz")
	params := &s3.GetObjectInput{
		Bucket: aws.String(s.config.AwsBucket),
		Key:    aws.String(key),
	}
	resp, err := s.svc.GetObject(params)
	if err != nil {
		return "", errors.Wrap(err, fmt.Sprintf("error downloading key: %s", key))
	}

	dir := path.Dir(fileName)
	err = os.MkdirAll(dir, os.ModeDir|os.ModePerm)
	if err != nil {
		return "", errors.Wrap(err, fmt.Sprintf("error creading dir %s", dir))
	}

	file, err := os.Create(fileName)
	if err != nil {
		return "", errors.Wrap(err, "error opening file")
	}

	reader, writer := io.Pipe()
	go func() {
		gr, err := gzip.NewReader(resp.Body)
		if err != nil {
			glog.Errorf("error creating new gzip reader")
			os.Exit(1)
		}
		io.Copy(writer, gr)
		gr.Close()
		writer.Close()
	}()

	var buf []byte
	buf = make([]byte, 1024)
	for {
		n, err := reader.Read(buf)
		if err != nil && err != io.EOF {
			return "", errors.Wrap(err, "error reading file")
		}
		if n == 0 {
			break
		}
		if _, writeErr := file.Write(buf[:n]); writeErr != nil {
			return "", errors.Wrap(err, "error writing file")
		}
		if err == io.EOF {
			break
		}
	}

	if err = reader.Close(); err != nil {
		return "", errors.Wrap(err, "error reading downloaded file")
	}
	if err = file.Close(); err != nil {
		return "", errors.Wrap(err, "error closing file")
	}
	return fileName, nil
}

// SnapshotHistory retrieves snapshot history from S3.
func (s *S3) SnapshotHistory() (*SnapshotHistory, error) {
	prefix := fmt.Sprintf("%s/%s", s.config.AwsBasePath, s.config.Keyspace)
	params := &s3.ListObjectsV2Input{
		Bucket: aws.String(s.config.AwsBucket),
		Prefix: aws.String(prefix),
	}
	h := NewSnapshotHistory()
	for {
		resp, err := s.svc.ListObjectsV2(params)
		if err != nil {
			return nil, errors.Wrap(err, "error listing from S3")
		}
		for _, obj := range resp.Contents {
			h.Add(*obj.Key)
		}
		if !*resp.IsTruncated {
			break
		}
		params.ContinuationToken = resp.NextContinuationToken
	}
	return h, nil
}
<|endoftext|>


<commit_before>package jpeg

/*
#include <stdio.h>
#include <stdlib.h>
#include "jpeglib.h"
#include "jpeg.h"

void error_panic(j_common_ptr cinfo);

static struct jpeg_compress_struct *new_compress(void) {
	struct jpeg_compress_struct *cinfo = (struct jpeg_compress_struct *) malloc(sizeof(struct jpeg_compress_struct));
	struct jpeg_error_mgr *jerr = (struct jpeg_error_mgr *)malloc(sizeof(struct jpeg_error_mgr));

	jpeg_std_error(jerr);
	jerr->error_exit = (void *)error_panic;
	jpeg_create_compress(cinfo);
	cinfo->err = jerr;

	return cinfo;
}

static void destroy_compress(struct jpeg_compress_struct *cinfo) {
	free(cinfo->err);
	jpeg_destroy_compress(cinfo);
	free(cinfo);
}

static void encode_gray(j_compress_ptr cinfo, JSAMPROW pix, int stride) {
	// Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data
	// this is a safe overestimate; we use the return value from
	// jpeg_read_raw_data to figure out what is the actual iMCU row count.
	JSAMPROW *rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);

	int v = 0;
	for (v = 0; v < cinfo->image_height; ) {
		// First fill in the pointers into the plane data buffers
		int h = 0;
		for (h = 0; h < DCTSIZE * cinfo->comp_info[0].v_samp_factor; h++) {
			rows[h] = &pix[stride * (v + h)];
		}
		// Get the data
		v += jpeg_write_raw_data(cinfo, &rows, DCTSIZE * cinfo->comp_info[0].v_samp_factor);
	}
}

static void encode_rgba(j_compress_ptr cinfo, unsigned char pix[], int stride) {
	JSAMPROW row_pointer[1];

	int v;
	for (v = 0; v < cinfo->image_height; v++) {
		row_pointer[0] = &pix[v * stride];
		jpeg_write_scanlines(cinfo, row_pointer, 1);
	}
}

static void encode_ycbcr(j_compress_ptr cinfo, JSAMPROW y_row, JSAMPROW cb_row, JSAMPROW cr_row, int y_stride, int c_stride, int color_v_div) {
	// Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data
	// this is a safe overestimate; we use the return value from
	// jpeg_read_raw_data to figure out what is the actual iMCU row count.
	JSAMPROW *y_rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);
	JSAMPROW *cb_rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);
	JSAMPROW *cr_rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);
	JSAMPARRAY image[] = { y_rows, cb_rows, cr_rows };

	int v = 0;
	for (v = 0; v < cinfo->image_height; ) {
		int h = 0;
		// First fill in the pointers into the plane data buffers
		for (h = 0; h <  DCTSIZE * cinfo->comp_info[0].v_samp_factor; h++) {
			y_rows[h] = &y_row[y_stride * (v + h)];
		}
		for (h = 0; h <  DCTSIZE * cinfo->comp_info[1].v_samp_factor; h++) {
			cb_rows[h] = &cb_row[c_stride * (v / color_v_div + h)];
			cr_rows[h] = &cr_row[c_stride * (v / color_v_div + h)];
		}
		// Get the data
		v += jpeg_write_raw_data(cinfo, image, DCTSIZE * cinfo->comp_info[0].v_samp_factor);
	}
}

*/
import "C"

import (
	"errors"
	"fmt"
	"image"
	"io"
	"unsafe"
)

// EncoderOptions specifies which settings to use during Compression.
type EncoderOptions struct {
	Quality        int
	OptimizeCoding bool
	DCTMethod      DCTMethod
}

// Encode encodes src image and writes into w as JPEG format data.
func Encode(w io.Writer, src image.Image, opt *EncoderOptions) (err error) {
	// Recover panic
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("JPEG error: %v", r)
			}
		}
	}()

	cinfo := C.new_compress()
	defer C.destroy_compress(cinfo)

	dstManager := makeDestinationManager(w, cinfo)
	defer releaseDestinationManager(dstManager)

	switch s := src.(type) {
	case *image.YCbCr:
		err = encodeYCbCr(cinfo, s, opt)
	case *image.Gray:
		err = encodeGray(cinfo, s, opt)
	case *image.RGBA:
		err = encodeRGBA(cinfo, s, opt)
	default:
		return errors.New("unsupported image type")
	}

	return
}

// encode image.YCbCr
func encodeYCbCr(cinfo *C.struct_jpeg_compress_struct, src *image.YCbCr, p *EncoderOptions) (err error) {
	// Set up compression parameters
	cinfo.image_width = C.JDIMENSION(src.Bounds().Dx())
	cinfo.image_height = C.JDIMENSION(src.Bounds().Dy())
	cinfo.input_components = 3
	cinfo.in_color_space = C.JCS_YCbCr

	C.jpeg_set_defaults(cinfo)
	setupEncoderOptions(cinfo, p)

	compInfo := (*[3]C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info))
	colorVDiv := 1
	switch src.SubsampleRatio {
	case image.YCbCrSubsampleRatio444:
		// 1x1,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 1
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
	case image.YCbCrSubsampleRatio440:
		// 1x2,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 2
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
		colorVDiv = 2
	case image.YCbCrSubsampleRatio422:
		// 2x1,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 1
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
	case image.YCbCrSubsampleRatio420:
		// 2x2,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 2
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
		colorVDiv = 2
	}

	// libjpeg raw data in is in planar format, which avoids unnecessary
	// planar->packed->planar conversions.
	cinfo.raw_data_in = C.TRUE

	// Start compression
	C.jpeg_start_compress(cinfo, C.TRUE)
	C.encode_ycbcr(
		cinfo,
		C.JSAMPROW(unsafe.Pointer(&src.Y[0])),
		C.JSAMPROW(unsafe.Pointer(&src.Cb[0])),
		C.JSAMPROW(unsafe.Pointer(&src.Cr[0])),
		C.int(src.YStride),
		C.int(src.CStride),
		C.int(colorVDiv),
	)
	C.jpeg_finish_compress(cinfo)
	return
}

// encode image.RGBA
func encodeRGBA(cinfo *C.struct_jpeg_compress_struct, src *image.RGBA, p *EncoderOptions) (err error) {
	// Set up compression parameters
	cinfo.image_width = C.JDIMENSION(src.Bounds().Dx())
	cinfo.image_height = C.JDIMENSION(src.Bounds().Dy())
	cinfo.input_components = 4
	cinfo.in_color_space = C.JCS_EXT_RGBA

	C.jpeg_set_defaults(cinfo)
	setupEncoderOptions(cinfo, p)

	// Start compression
	C.jpeg_start_compress(cinfo, C.TRUE)
	C.encode_rgba(cinfo, (*C.uchar)(unsafe.Pointer(&src.Pix[0])), C.int(src.Stride))
	C.jpeg_finish_compress(cinfo)
	return
}

// encode image.Gray
func encodeGray(cinfo *C.struct_jpeg_compress_struct, src *image.Gray, p *EncoderOptions) (err error) {
	// Set up compression parameters
	cinfo.image_width = C.JDIMENSION(src.Bounds().Dx())
	cinfo.image_height = C.JDIMENSION(src.Bounds().Dy())
	cinfo.input_components = 1
	cinfo.in_color_space = C.JCS_GRAYSCALE

	C.jpeg_set_defaults(cinfo)
	setupEncoderOptions(cinfo, p)

	compInfo := (*C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info))
	compInfo.h_samp_factor, compInfo.v_samp_factor = 1, 1

	// libjpeg raw data in is in planar format, which avoids unnecessary
	// planar->packed->planar conversions.
	cinfo.raw_data_in = C.TRUE

	// Start compression
	C.jpeg_start_compress(cinfo, C.TRUE)
	C.encode_gray(cinfo, C.JSAMPROW(unsafe.Pointer(&src.Pix[0])), C.int(src.Stride))
	C.jpeg_finish_compress(cinfo)
	return
}

func setupEncoderOptions(cinfo *C.struct_jpeg_compress_struct, opt *EncoderOptions) {
	C.jpeg_set_quality(cinfo, C.int(opt.Quality), C.TRUE)
	if opt.OptimizeCoding {
		cinfo.optimize_coding = C.TRUE
	} else {
		cinfo.optimize_coding = C.FALSE
	}
	cinfo.dct_method = C.J_DCT_METHOD(opt.DCTMethod)
}
<commit_msg>Add to check JCS_EXT_RGBA support.<commit_after>package jpeg

/*
#include <stdio.h>
#include <stdlib.h>
#include "jpeglib.h"
#include "jpeg.h"

void error_panic(j_common_ptr cinfo);

static struct jpeg_compress_struct *new_compress(void) {
	struct jpeg_compress_struct *cinfo = (struct jpeg_compress_struct *) malloc(sizeof(struct jpeg_compress_struct));
	struct jpeg_error_mgr *jerr = (struct jpeg_error_mgr *)malloc(sizeof(struct jpeg_error_mgr));

	jpeg_std_error(jerr);
	jerr->error_exit = (void *)error_panic;
	jpeg_create_compress(cinfo);
	cinfo->err = jerr;

	return cinfo;
}

static void destroy_compress(struct jpeg_compress_struct *cinfo) {
	free(cinfo->err);
	jpeg_destroy_compress(cinfo);
	free(cinfo);
}

static void encode_gray(j_compress_ptr cinfo, JSAMPROW pix, int stride) {
	// Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data
	// this is a safe overestimate; we use the return value from
	// jpeg_read_raw_data to figure out what is the actual iMCU row count.
	JSAMPROW *rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);

	int v = 0;
	for (v = 0; v < cinfo->image_height; ) {
		// First fill in the pointers into the plane data buffers
		int h = 0;
		for (h = 0; h < DCTSIZE * cinfo->comp_info[0].v_samp_factor; h++) {
			rows[h] = &pix[stride * (v + h)];
		}
		// Get the data
		v += jpeg_write_raw_data(cinfo, &rows, DCTSIZE * cinfo->comp_info[0].v_samp_factor);
	}
}

static void encode_rgba(j_compress_ptr cinfo, unsigned char pix[], int stride) {
	JSAMPROW row_pointer[1];

	int v;
	for (v = 0; v < cinfo->image_height; v++) {
		row_pointer[0] = &pix[v * stride];
		jpeg_write_scanlines(cinfo, row_pointer, 1);
	}
}

static void encode_ycbcr(j_compress_ptr cinfo, JSAMPROW y_row, JSAMPROW cb_row, JSAMPROW cr_row, int y_stride, int c_stride, int color_v_div) {
	// Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data
	// this is a safe overestimate; we use the return value from
	// jpeg_read_raw_data to figure out what is the actual iMCU row count.
	JSAMPROW *y_rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);
	JSAMPROW *cb_rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);
	JSAMPROW *cr_rows = alloca(sizeof(JSAMPROW) * ALIGN_SIZE);
	JSAMPARRAY image[] = { y_rows, cb_rows, cr_rows };

	int v = 0;
	for (v = 0; v < cinfo->image_height; ) {
		int h = 0;
		// First fill in the pointers into the plane data buffers
		for (h = 0; h <  DCTSIZE * cinfo->comp_info[0].v_samp_factor; h++) {
			y_rows[h] = &y_row[y_stride * (v + h)];
		}
		for (h = 0; h <  DCTSIZE * cinfo->comp_info[1].v_samp_factor; h++) {
			cb_rows[h] = &cb_row[c_stride * (v / color_v_div + h)];
			cr_rows[h] = &cr_row[c_stride * (v / color_v_div + h)];
		}
		// Get the data
		v += jpeg_write_raw_data(cinfo, image, DCTSIZE * cinfo->comp_info[0].v_samp_factor);
	}
}

*/
import "C"

import (
	"errors"
	"fmt"
	"image"
	"io"
	"unsafe"
)

// EncoderOptions specifies which settings to use during Compression.
type EncoderOptions struct {
	Quality        int
	OptimizeCoding bool
	DCTMethod      DCTMethod
}

// Encode encodes src image and writes into w as JPEG format data.
func Encode(w io.Writer, src image.Image, opt *EncoderOptions) (err error) {
	// Recover panic
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("JPEG error: %v", r)
			}
		}
	}()

	cinfo := C.new_compress()
	defer C.destroy_compress(cinfo)

	dstManager := makeDestinationManager(w, cinfo)
	defer releaseDestinationManager(dstManager)

	switch s := src.(type) {
	case *image.YCbCr:
		err = encodeYCbCr(cinfo, s, opt)
	case *image.Gray:
		err = encodeGray(cinfo, s, opt)
	case *image.RGBA:
		err = encodeRGBA(cinfo, s, opt)
	default:
		return errors.New("unsupported image type")
	}

	return
}

// encode image.YCbCr
func encodeYCbCr(cinfo *C.struct_jpeg_compress_struct, src *image.YCbCr, p *EncoderOptions) (err error) {
	// Set up compression parameters
	cinfo.image_width = C.JDIMENSION(src.Bounds().Dx())
	cinfo.image_height = C.JDIMENSION(src.Bounds().Dy())
	cinfo.input_components = 3
	cinfo.in_color_space = C.JCS_YCbCr

	C.jpeg_set_defaults(cinfo)
	setupEncoderOptions(cinfo, p)

	compInfo := (*[3]C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info))
	colorVDiv := 1
	switch src.SubsampleRatio {
	case image.YCbCrSubsampleRatio444:
		// 1x1,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 1
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
	case image.YCbCrSubsampleRatio440:
		// 1x2,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 2
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
		colorVDiv = 2
	case image.YCbCrSubsampleRatio422:
		// 2x1,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 1
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
	case image.YCbCrSubsampleRatio420:
		// 2x2,1x1,1x1
		compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 2
		compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1
		compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1
		colorVDiv = 2
	}

	// libjpeg raw data in is in planar format, which avoids unnecessary
	// planar->packed->planar conversions.
	cinfo.raw_data_in = C.TRUE

	// Start compression
	C.jpeg_start_compress(cinfo, C.TRUE)
	C.encode_ycbcr(
		cinfo,
		C.JSAMPROW(unsafe.Pointer(&src.Y[0])),
		C.JSAMPROW(unsafe.Pointer(&src.Cb[0])),
		C.JSAMPROW(unsafe.Pointer(&src.Cr[0])),
		C.int(src.YStride),
		C.int(src.CStride),
		C.int(colorVDiv),
	)
	C.jpeg_finish_compress(cinfo)
	return
}

// encode image.RGBA
func encodeRGBA(cinfo *C.struct_jpeg_compress_struct, src *image.RGBA, p *EncoderOptions) (err error) {
	// Set up compression parameters
	cinfo.image_width = C.JDIMENSION(src.Bounds().Dx())
	cinfo.image_height = C.JDIMENSION(src.Bounds().Dy())
	cinfo.input_components = 4
	cinfo.in_color_space = C.getJCS_EXT_RGBA()
	if cinfo.in_color_space == C.JCS_UNKNOWN {
		return nil, errors.New("JCS_EXT_RGBA is not supported (probably built without libjpeg-trubo)")
	}

	C.jpeg_set_defaults(cinfo)
	setupEncoderOptions(cinfo, p)

	// Start compression
	C.jpeg_start_compress(cinfo, C.TRUE)
	C.encode_rgba(cinfo, (*C.uchar)(unsafe.Pointer(&src.Pix[0])), C.int(src.Stride))
	C.jpeg_finish_compress(cinfo)
	return
}

// encode image.Gray
func encodeGray(cinfo *C.struct_jpeg_compress_struct, src *image.Gray, p *EncoderOptions) (err error) {
	// Set up compression parameters
	cinfo.image_width = C.JDIMENSION(src.Bounds().Dx())
	cinfo.image_height = C.JDIMENSION(src.Bounds().Dy())
	cinfo.input_components = 1
	cinfo.in_color_space = C.JCS_GRAYSCALE

	C.jpeg_set_defaults(cinfo)
	setupEncoderOptions(cinfo, p)

	compInfo := (*C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info))
	compInfo.h_samp_factor, compInfo.v_samp_factor = 1, 1

	// libjpeg raw data in is in planar format, which avoids unnecessary
	// planar->packed->planar conversions.
	cinfo.raw_data_in = C.TRUE

	// Start compression
	C.jpeg_start_compress(cinfo, C.TRUE)
	C.encode_gray(cinfo, C.JSAMPROW(unsafe.Pointer(&src.Pix[0])), C.int(src.Stride))
	C.jpeg_finish_compress(cinfo)
	return
}

func setupEncoderOptions(cinfo *C.struct_jpeg_compress_struct, opt *EncoderOptions) {
	C.jpeg_set_quality(cinfo, C.int(opt.Quality), C.TRUE)
	if opt.OptimizeCoding {
		cinfo.optimize_coding = C.TRUE
	} else {
		cinfo.optimize_coding = C.FALSE
	}
	cinfo.dct_method = C.J_DCT_METHOD(opt.DCTMethod)
}
<|endoftext|>