<commit_before>package models

import (
	"encoding/json"
	"errors"
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/go-utils/parseutil"
	"github.com/bitrise-io/go-utils/pointers"
)

const (
	// OptionsKey ...
	OptionsKey = "opts"
)

const (
	// DefaultIsExpand ...
	DefaultIsExpand = true
	//DefaultIsRequired ...
	DefaultIsRequired = false
	// DefaultIsDontChangeValue ...
	DefaultIsDontChangeValue = false
	// DefaultIsTemplat ...
	DefaultIsTemplat = false
)

// CreateFromJSON ...
func (envList EnvsJSONListModel) CreateFromJSON(jsonStr string) (EnvsJSONListModel, error) {
	list := EnvsJSONListModel{}
	if err := json.Unmarshal([]byte(jsonStr), &list); err != nil {
		return EnvsJSONListModel{}, err
	}
	return list, nil
}

// GetKeyValuePair ...
func (env EnvironmentItemModel) GetKeyValuePair() (string, string, error) {
	if len(env) > 2 {
		return "", "", fmt.Errorf("Invalid env: more than 2 fields: %#v", env)
	}

	retKey := ""
	retValue := ""

	for key, value := range env {
		if key != OptionsKey {
			if retKey != "" {
				return "", "", fmt.Errorf("Invalid env: more than 1 key-value field found: %#v", env)
			}

			valueStr, ok := value.(string)
			if !ok {
				if value == nil {
					valueStr = ""
				} else {
					valueStr = parseutil.CastToString(value)
					if valueStr == "" {
						return "", "", fmt.Errorf("Invalid value, not a string (key:%#v) (value:%#v)", key, value)
					}
				}
			}

			retKey = key
			retValue = valueStr
		}
	}

	if retKey == "" {
		return "", "", errors.New("Invalid env: no envKey specified!")
	}

	return retKey, retValue, nil
}

// ParseFromInterfaceMap ...
func (envSerModel *EnvironmentItemOptionsModel) ParseFromInterfaceMap(input map[string]interface{}) error {
	for keyStr, value := range input {
		log.Debugf("  ** processing (key:%#v) (value:%#v) (envSerModel:%#v)", keyStr, value, envSerModel)
		switch keyStr {
		case "title":
			castedValue, ok := value.(string)
			if !ok {
				return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
			}
			envSerModel.Title = pointers.NewStringPtr(castedValue)
		case "description":
			castedValue, ok := value.(string)
			if !ok {
				return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
			}
			envSerModel.Description = pointers.NewStringPtr(castedValue)
		case "summary":
			castedValue, ok := value.(string)
			if !ok {
				return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
			}
			envSerModel.Summary = pointers.NewStringPtr(castedValue)
		case "value_options":
			castedValue, ok := value.([]string)
			if !ok {
				// try with []interface{} instead and cast the
				//  items to string
				castedValue = []string{}
				interfArr, ok := value.([]interface{})
				if !ok {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
				for _, interfItm := range interfArr {
					castedItm, ok := interfItm.(string)
					if !ok {
						castedItm = parseutil.CastToString(interfItm)
						if castedItm == "" {
							return fmt.Errorf("Invalid value in value_options (%#v), not a string: %#v", interfArr, interfItm)
						}
					}
					castedValue = append(castedValue, castedItm)
				}
			}
			envSerModel.ValueOptions = castedValue
		case "is_required":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsRequired = pointers.NewBoolPtr(castedValue)
		case "is_expand":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsExpand = pointers.NewBoolPtr(castedValue)
		case "is_dont_change_value":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsDontChangeValue = pointers.NewBoolPtr(castedValue)
		case "is_template":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsTemplate = pointers.NewBoolPtr(castedValue)
		default:
			return fmt.Errorf("Not supported key found in options: %#v", keyStr)
		}
	}
	return nil
}

// GetOptions ...
func (env EnvironmentItemModel) GetOptions() (EnvironmentItemOptionsModel, error) {
	value, found := env[OptionsKey]
	if !found {
		return EnvironmentItemOptionsModel{}, nil
	}

	envItmCasted, ok := value.(EnvironmentItemOptionsModel)
	if ok {
		return envItmCasted, nil
	}

	log.Debugf(" * processing env:%#v", env)

	// if it's read from a file (YAML/JSON) then it's most likely not the proper type
	//  so cast it from the generic interface-interface map
	normalizedOptsInterfaceMap := make(map[string]interface{})
	isNormalizeOK := false
	if optionsInterfaceMap, ok := value.(map[interface{}]interface{}); ok {
		// Try to normalize every key to String
		for key, value := range optionsInterfaceMap {
			keyStr, ok := key.(string)
			if !ok {
				return EnvironmentItemOptionsModel{}, fmt.Errorf("Failed to cask Options key to String: %#v", key)
			}
			normalizedOptsInterfaceMap[keyStr] = value
		}
		isNormalizeOK = true
	} else {
		if castedTmp, ok := value.(map[string]interface{}); ok {
			normalizedOptsInterfaceMap = castedTmp
			isNormalizeOK = true
		}
	}

	if isNormalizeOK {
		options := EnvironmentItemOptionsModel{}
		err := options.ParseFromInterfaceMap(normalizedOptsInterfaceMap)
		if err != nil {
			return EnvironmentItemOptionsModel{}, err
		}

		log.Debugf("Parsed options: %#v\n", options)
		return options, nil
	}

	return EnvironmentItemOptionsModel{}, fmt.Errorf("Invalid options (value:%#v) - failed to cast", value)
}

// Normalize ...
func (env *EnvironmentItemModel) Normalize() error {
	opts, err := env.GetOptions()
	if err != nil {
		return err
	}
	(*env)[OptionsKey] = opts
	return nil
}

// FillMissingDefaults ...
func (env *EnvironmentItemModel) FillMissingDefaults() error {
	options, err := env.GetOptions()
	if err != nil {
		return err
	}
	if options.Title == nil {
		options.Title = pointers.NewStringPtr("")
	}
	if options.Description == nil {
		options.Description = pointers.NewStringPtr("")
	}
	if options.Summary == nil {
		options.Summary = pointers.NewStringPtr("")
	}
	if options.IsRequired == nil {
		options.IsRequired = pointers.NewBoolPtr(DefaultIsRequired)
	}
	if options.IsExpand == nil {
		options.IsExpand = pointers.NewBoolPtr(DefaultIsExpand)
	}
	if options.IsDontChangeValue == nil {
		options.IsDontChangeValue = pointers.NewBoolPtr(DefaultIsDontChangeValue)
	}
	if options.IsTemplate == nil {
		options.IsTemplate = pointers.NewBoolPtr(DefaultIsTemplat)
	}
	(*env)[OptionsKey] = options
	return nil
}

// Validate ...
func (env EnvironmentItemModel) Validate() error {
	key, _, err := env.GetKeyValuePair()
	if err != nil {
		return err
	}
	if key == "" {
		return errors.New("Invalid environment: empty env_key")
	}
	_, err = env.GetOptions()
	if err != nil {
		return err
	}
	return nil
}

// NormalizeValidateFillDefaults ...
func (env EnvironmentItemModel) NormalizeValidateFillDefaults() error {
	if err := env.Normalize(); err != nil {
		return err
	}

	if err := env.Validate(); err != nil {
		return err
	}

	if err := env.FillMissingDefaults(); err != nil {
		return err
	}
	return nil
}
<commit_msg>`DefaultIsTemplate` - typo fix (missing `e`)<commit_after>package models

import (
	"encoding/json"
	"errors"
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/go-utils/parseutil"
	"github.com/bitrise-io/go-utils/pointers"
)

const (
	// OptionsKey ...
	OptionsKey = "opts"
)

const (
	// DefaultIsExpand ...
	DefaultIsExpand = true
	//DefaultIsRequired ...
	DefaultIsRequired = false
	// DefaultIsDontChangeValue ...
	DefaultIsDontChangeValue = false
	// DefaultIsTemplate ...
	DefaultIsTemplate = false
)

// CreateFromJSON ...
func (envList EnvsJSONListModel) CreateFromJSON(jsonStr string) (EnvsJSONListModel, error) {
	list := EnvsJSONListModel{}
	if err := json.Unmarshal([]byte(jsonStr), &list); err != nil {
		return EnvsJSONListModel{}, err
	}
	return list, nil
}

// GetKeyValuePair ...
func (env EnvironmentItemModel) GetKeyValuePair() (string, string, error) {
	if len(env) > 2 {
		return "", "", fmt.Errorf("Invalid env: more than 2 fields: %#v", env)
	}

	retKey := ""
	retValue := ""

	for key, value := range env {
		if key != OptionsKey {
			if retKey != "" {
				return "", "", fmt.Errorf("Invalid env: more than 1 key-value field found: %#v", env)
			}

			valueStr, ok := value.(string)
			if !ok {
				if value == nil {
					valueStr = ""
				} else {
					valueStr = parseutil.CastToString(value)
					if valueStr == "" {
						return "", "", fmt.Errorf("Invalid value, not a string (key:%#v) (value:%#v)", key, value)
					}
				}
			}

			retKey = key
			retValue = valueStr
		}
	}

	if retKey == "" {
		return "", "", errors.New("Invalid env: no envKey specified!")
	}

	return retKey, retValue, nil
}

// ParseFromInterfaceMap ...
func (envSerModel *EnvironmentItemOptionsModel) ParseFromInterfaceMap(input map[string]interface{}) error {
	for keyStr, value := range input {
		log.Debugf("  ** processing (key:%#v) (value:%#v) (envSerModel:%#v)", keyStr, value, envSerModel)
		switch keyStr {
		case "title":
			castedValue, ok := value.(string)
			if !ok {
				return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
			}
			envSerModel.Title = pointers.NewStringPtr(castedValue)
		case "description":
			castedValue, ok := value.(string)
			if !ok {
				return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
			}
			envSerModel.Description = pointers.NewStringPtr(castedValue)
		case "summary":
			castedValue, ok := value.(string)
			if !ok {
				return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
			}
			envSerModel.Summary = pointers.NewStringPtr(castedValue)
		case "value_options":
			castedValue, ok := value.([]string)
			if !ok {
				// try with []interface{} instead and cast the
				//  items to string
				castedValue = []string{}
				interfArr, ok := value.([]interface{})
				if !ok {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
				for _, interfItm := range interfArr {
					castedItm, ok := interfItm.(string)
					if !ok {
						castedItm = parseutil.CastToString(interfItm)
						if castedItm == "" {
							return fmt.Errorf("Invalid value in value_options (%#v), not a string: %#v", interfArr, interfItm)
						}
					}
					castedValue = append(castedValue, castedItm)
				}
			}
			envSerModel.ValueOptions = castedValue
		case "is_required":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsRequired = pointers.NewBoolPtr(castedValue)
		case "is_expand":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsExpand = pointers.NewBoolPtr(castedValue)
		case "is_dont_change_value":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsDontChangeValue = pointers.NewBoolPtr(castedValue)
		case "is_template":
			castedValue, ok := value.(bool)
			if !ok {
				castedStr := parseutil.CastToString(value)
				if castedStr != "" {
					casted, err := parseutil.ParseBool(castedStr)
					if err != nil {
						return fmt.Errorf("Faild to parse bool (key:%s): %#v, err: %s", keyStr, value, err)
					}
					castedValue = casted
				} else {
					return fmt.Errorf("Invalid value type (key:%s): %#v", keyStr, value)
				}
			}
			envSerModel.IsTemplate = pointers.NewBoolPtr(castedValue)
		default:
			return fmt.Errorf("Not supported key found in options: %#v", keyStr)
		}
	}
	return nil
}

// GetOptions ...
func (env EnvironmentItemModel) GetOptions() (EnvironmentItemOptionsModel, error) {
	value, found := env[OptionsKey]
	if !found {
		return EnvironmentItemOptionsModel{}, nil
	}

	envItmCasted, ok := value.(EnvironmentItemOptionsModel)
	if ok {
		return envItmCasted, nil
	}

	log.Debugf(" * processing env:%#v", env)

	// if it's read from a file (YAML/JSON) then it's most likely not the proper type
	//  so cast it from the generic interface-interface map
	normalizedOptsInterfaceMap := make(map[string]interface{})
	isNormalizeOK := false
	if optionsInterfaceMap, ok := value.(map[interface{}]interface{}); ok {
		// Try to normalize every key to String
		for key, value := range optionsInterfaceMap {
			keyStr, ok := key.(string)
			if !ok {
				return EnvironmentItemOptionsModel{}, fmt.Errorf("Failed to cask Options key to String: %#v", key)
			}
			normalizedOptsInterfaceMap[keyStr] = value
		}
		isNormalizeOK = true
	} else {
		if castedTmp, ok := value.(map[string]interface{}); ok {
			normalizedOptsInterfaceMap = castedTmp
			isNormalizeOK = true
		}
	}

	if isNormalizeOK {
		options := EnvironmentItemOptionsModel{}
		err := options.ParseFromInterfaceMap(normalizedOptsInterfaceMap)
		if err != nil {
			return EnvironmentItemOptionsModel{}, err
		}

		log.Debugf("Parsed options: %#v\n", options)
		return options, nil
	}

	return EnvironmentItemOptionsModel{}, fmt.Errorf("Invalid options (value:%#v) - failed to cast", value)
}

// Normalize ...
func (env *EnvironmentItemModel) Normalize() error {
	opts, err := env.GetOptions()
	if err != nil {
		return err
	}
	(*env)[OptionsKey] = opts
	return nil
}

// FillMissingDefaults ...
func (env *EnvironmentItemModel) FillMissingDefaults() error {
	options, err := env.GetOptions()
	if err != nil {
		return err
	}
	if options.Title == nil {
		options.Title = pointers.NewStringPtr("")
	}
	if options.Description == nil {
		options.Description = pointers.NewStringPtr("")
	}
	if options.Summary == nil {
		options.Summary = pointers.NewStringPtr("")
	}
	if options.IsRequired == nil {
		options.IsRequired = pointers.NewBoolPtr(DefaultIsRequired)
	}
	if options.IsExpand == nil {
		options.IsExpand = pointers.NewBoolPtr(DefaultIsExpand)
	}
	if options.IsDontChangeValue == nil {
		options.IsDontChangeValue = pointers.NewBoolPtr(DefaultIsDontChangeValue)
	}
	if options.IsTemplate == nil {
		options.IsTemplate = pointers.NewBoolPtr(DefaultIsTemplate)
	}
	(*env)[OptionsKey] = options
	return nil
}

// Validate ...
func (env EnvironmentItemModel) Validate() error {
	key, _, err := env.GetKeyValuePair()
	if err != nil {
		return err
	}
	if key == "" {
		return errors.New("Invalid environment: empty env_key")
	}
	_, err = env.GetOptions()
	if err != nil {
		return err
	}
	return nil
}

// NormalizeValidateFillDefaults ...
func (env EnvironmentItemModel) NormalizeValidateFillDefaults() error {
	if err := env.Normalize(); err != nil {
		return err
	}

	if err := env.Validate(); err != nil {
		return err
	}

	if err := env.FillMissingDefaults(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package embedded

import (
	"fmt"
)

type SPI_regs struct {
	rxdata  uint32
	txdata  uint32
	control uint32
	config  uint32
	intr    uint32
	dma     uint32
	status  uint32
	period  uint32
	test    uint32
	_       [7]uint32
	msgdata uint32
}

//either mosi, miso, sclk, or cs
type SPI_pin struct {
	name   string
	alt    uint8
	muxctl *uint32
	padctl *uint32
}

type SPI_periph struct {
	mosi       SPI_pin
	miso       SPI_pin
	sclk       SPI_pin
	cs         []SPI_pin
	regs       *SPI_regs
	mode       uint32
	frequency  uint32
	datalength uint32
}

//SPI has 3 types of modes which affect the polarity of the clock and the resting state of the data signals
//data length is how many bits each SPI frame contains. 7,8,16 are common amounts

//the imx6 can support up to 2^12 bits in a single frame!
func (spi *SPI_periph) Begin(mode, freq, datalength, channel uint32) {
	//put the gpio pins on push/pull mode with their appropriate alternate functions
	*spi.mosi.muxctl = makeGPIOmuxconfig(spi.mosi.alt)
	*spi.mosi.padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)

	*spi.miso.muxctl = makeGPIOmuxconfig(spi.miso.alt)
	*spi.miso.padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)

	*spi.sclk.muxctl = makeGPIOmuxconfig(spi.sclk.alt)
	*spi.sclk.padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)

	for i := 0; i < len(spi.cs); i++ {
		*spi.cs[i].muxctl = makeGPIOmuxconfig(spi.cs[i].alt)
		*spi.cs[i].padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)
	}

	//ungate the module clock
	//Put 0x3F into CCM_CCGR1
	//CCM_CCGR1 := ((*uint32)(unsafe.Pointer(uintptr(0x20C406C))))
	//*CCM_CCGR1 |= 0x3F

	//configure the SPI registers for the freq, mode, and datalength
	spi.regs.control = 0
	spi.regs.control |= datalength << 20
	if channel > 1 {
		fmt.Printf("bad spi channel, abort\n")
		return
	}
	spi.regs.control |= channel << 18

	//use a post divider
	spi.regs.control |= freq << 8

	//everyone is in master mode
	spi.regs.control |= 0xF << 4

	spi.regs.control |= 1 << 3

	spi.regs.config = 0
	//spi.regs.config |= 0xF << 20
	spi.regs.config |= 0xF << 16
	if mode == 0 {
	} else if mode == 3 {
		spi.regs.config |= 0xF << 4
		spi.regs.config |= 0xF
	} else {
		fmt.Printf("unsupported mode %d. abort\n", mode)
	}
	spi.regs.config |= 0xF << 12

	//toggle cs on every burst
	spi.regs.config |= 0xF << 8

	spi.regs.intr = 0

	spi.regs.control |= 1

	spi.regs.status = 0xff

	spi.mode = mode
	spi.frequency = freq
	spi.datalength = datalength
}

func (spi *SPI_periph) Send(data uint32) {
	mask := uint32(1<<spi.datalength) - uint32(1)
	data = data & mask

	//wait for tx fifo to have space
	//for spi.regs.status&0x2 != 0 {
	//}

	spi.regs.txdata = data
}
<commit_msg>try on robot<commit_after>package embedded

import (
	"fmt"
)

type SPI_regs struct {
	rxdata  uint32
	txdata  uint32
	control uint32
	config  uint32
	intr    uint32
	dma     uint32
	status  uint32
	period  uint32
	test    uint32
	_       [7]uint32
	msgdata uint32
}

//either mosi, miso, sclk, or cs
type SPI_pin struct {
	name   string
	alt    uint8
	muxctl *uint32
	padctl *uint32
}

type SPI_periph struct {
	mosi       SPI_pin
	miso       SPI_pin
	sclk       SPI_pin
	cs         []SPI_pin
	regs       *SPI_regs
	mode       uint32
	frequency  uint32
	datalength uint32
}

//SPI has 3 types of modes which affect the polarity of the clock and the resting state of the data signals
//data length is how many bits each SPI frame contains. 7,8,16 are common amounts

//the imx6 can support up to 2^12 bits in a single frame!
func (spi *SPI_periph) Begin(mode, freq, datalength, channel uint32) {
	//put the gpio pins on push/pull mode with their appropriate alternate functions
	*spi.mosi.muxctl = makeGPIOmuxconfig(spi.mosi.alt)
	*spi.mosi.padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)

	*spi.miso.muxctl = makeGPIOmuxconfig(spi.miso.alt)
	*spi.miso.padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)

	*spi.sclk.muxctl = makeGPIOmuxconfig(spi.sclk.alt)
	*spi.sclk.padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)

	for i := 0; i < len(spi.cs); i++ {
		*spi.cs[i].muxctl = makeGPIOmuxconfig(spi.cs[i].alt)
		*spi.cs[i].padctl = makeGPIOpadconfig(1, PULLDOWN_100K, 1, 1, 0, SPEED_FAST, DRIVE_260R, SLEW_FAST)
	}

	//ungate the module clock
	//Put 0x3F into CCM_CCGR1
	//CCM_CCGR1 := ((*uint32)(unsafe.Pointer(uintptr(0x20C406C))))
	//*CCM_CCGR1 |= 0x3F

	//configure the SPI registers for the freq, mode, and datalength
	spi.regs.control = 0
	spi.regs.control |= datalength << 20
	if channel > 1 {
		fmt.Printf("bad spi channel, abort\n")
		return
	}
	spi.regs.control |= channel << 18

	//use a post divider
	spi.regs.control |= freq << 8

	//everyone is in master mode
	spi.regs.control |= 0xF << 4

	spi.regs.control |= 1 << 3

	spi.regs.config = 0
	//spi.regs.config |= 0xF << 20
	spi.regs.config |= 0xF << 16
	if mode == 0 {
	} else if mode == 3 {
		spi.regs.config |= 0xF << 4
		spi.regs.config |= 0xF
	} else {
		fmt.Printf("unsupported mode %d. abort\n", mode)
	}
	spi.regs.config |= 0xF << 12

	//toggle cs on every burst
	spi.regs.config |= 0xF << 8

	spi.regs.intr = 0

	spi.regs.control |= 1

	spi.regs.status = 0xff

	spi.mode = mode
	spi.frequency = freq
	spi.datalength = datalength
}

//assumes datalength < 32bits
func (spi *SPI_periph) Send(data uint32) {
	mask := uint32(1<<spi.datalength) - uint32(1)
	data = data & mask

	//wait for tx fifo to have space
	//for spi.regs.status&0x2 != 0 {
	//}

	spi.regs.txdata = data
}

//assumes datalength < 32 bits
func (spi *SPI_periph) Exchange(data uint32) uint32 {
	mask := uint32(1<<spi.datalength) - uint32(1)
	data = data & mask
	spi.regs.txdata = data

	//wait for rx fifo to get data
	for spi.regs.status&(0x1<<3) == 0 {
	}
	return spi.regs.rxdata
}
<|endoftext|>


<commit_before>package mdata

import (
	"testing"
	"time"

	"github.com/grafana/metrictank/cluster"
	"github.com/grafana/metrictank/conf"
	"github.com/grafana/metrictank/mdata/cache"
	"github.com/grafana/metrictank/test"
	"github.com/raintank/schema"
)

type testcase struct {
	ts       uint32
	span     uint32
	boundary uint32
}

func TestAggBoundary(t *testing.T) {
	cases := []testcase{
		{1, 120, 120},
		{50, 120, 120},
		{118, 120, 120},
		{119, 120, 120},
		{120, 120, 120},
		{121, 120, 240},
		{122, 120, 240},
		{150, 120, 240},
		{237, 120, 240},
		{238, 120, 240},
		{239, 120, 240},
		{240, 120, 240},
		{241, 120, 360},
	}
	for _, c := range cases {
		if ret := AggBoundary(c.ts, c.span); ret != c.boundary {
			t.Fatalf("aggBoundary for ts %d with span %d should be %d, not %d", c.ts, c.span, c.boundary, ret)
		}
	}
}

// note that values don't get "committed" to the metric until the aggregation interval is complete
func TestAggregator(t *testing.T) {
	cluster.Init("default", "test", time.Now(), "http", 6060)
	compare := func(key string, metric Metric, expected []schema.Point) {
		cluster.Manager.SetPrimary(true)
		res, err := metric.Get(0, 1000)

		if err != nil {
			t.Fatalf("expected err nil, got %v", err)
		}

		got := make([]schema.Point, 0, len(expected))
		for _, iter := range res.Iters {
			for iter.Next() {
				ts, val := iter.Values()
				got = append(got, schema.Point{Val: val, Ts: ts})
			}
		}
		if len(got) != len(expected) {
			t.Fatalf("output for testcase %s mismatch: expected: %v points, got: %v", key, len(expected), len(got))

		} else {
			for i, g := range got {
				exp := expected[i]
				if exp.Val != g.Val || exp.Ts != g.Ts {
					t.Fatalf("output for testcase %s mismatch at point %d: expected: %v, got: %v", key, i, exp, g)
				}
			}
		}
		cluster.Manager.SetPrimary(false)
	}
	ret := conf.NewRetentionMT(60, 86400, 120, 10, 0)
	aggs := conf.Aggregation{
		AggregationMethod: []conf.Method{conf.Avg, conf.Min, conf.Max, conf.Sum, conf.Lst},
	}

	agg := NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(0), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	expected := []schema.Point{}
	compare("simple-min-unfinished", agg.minMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(130, 130)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-one-block", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 140 means points before chunk starting at 240 are discarded
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 140)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	// this point is not flushed to agg.minMetric because no point after it with a timestamp
	// crossing the aggregation boundary is added (aggregation span here is 60)
	agg.Add(130, 130)
	expected = []schema.Point{}
	compare("simple-min-ingest-from-all-before-next-chunk", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 115 means points before chunk starting at 120 are discarded
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 115)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	// this point is not flushed to agg.minMetric for the same reason as in the previous test
	agg.Add(130, 130)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-ingest-from-one-in-next-chunk", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 120 means points before chunk starting at 120 are discarded
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 120)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	// this point is not flushed to agg.minMetric for the same reason as in the previous test
	agg.Add(130, 130)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-ingest-from-on-chunk-boundary", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 170 means points before chunk starting at 240 are discarded
	// but, each aggregation contains 60s of data, so the point at 240 covers raw data from 181..240

	// raw data         :   1..120 121..180 181..240 241..300 301..360
	// discarded data   :   xxxxxxxxxxxxxxxx
	// aggregated points:      120      180      240      300      360
	// chunks by t0     :      120               240      300      360
	// discarded chunk  :   xxxxxxxxxxxxxxxxxxxxx
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 170)
	agg.Add(1, 1.1)
	agg.Add(119, 119)
	agg.Add(120, 120)
	agg.Add(121, 121)
	agg.Add(179, 179)
	agg.Add(180, 180)
	agg.Add(181, 181)
	agg.Add(220, 220)
	agg.Add(230, 230)
	agg.Add(231, 231)
	agg.Add(233, 233)
	agg.Add(239, 239)
	agg.Add(240, 240)
	agg.Add(245, 245)
	agg.Add(249, 249)
	agg.Add(250, 250)
	agg.Add(299, 299)
	agg.Add(300, 300)
	// these points are not flushed to agg.sumMetric for the same reason as in the previous test
	agg.Add(301, 0)
	agg.Add(320, 0)

	expected = []schema.Point{
		{Val: 181 + 220 + 230 + 231 + 233 + 239 + 240, Ts: 240},
		{Val: 245 + 249 + 250 + 299 + 300, Ts: 300},
	}
	compare("multi-sum-ingest-from-one-in-next-chunk", agg.sumMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(2), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(120, 4)
	expected = []schema.Point{
		{Val: 4, Ts: 120},
	}
	compare("simple-min-one-block-done-cause-last-point-just-right", agg.minMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(3), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(150, 1.123)
	agg.Add(180, 1)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
		{Val: 1, Ts: 180},
	}
	compare("simple-min-two-blocks-done-cause-last-point-just-right", agg.minMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(4), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(190, 2451.123)
	agg.Add(200, 1451.123)
	agg.Add(220, 978894.445)
	agg.Add(250, 1)
	compare("simple-min-skip-a-block", agg.minMetric, []schema.Point{
		{Val: 5, Ts: 120},
		{Val: 1451.123, Ts: 240},
	})
	compare("simple-max-skip-a-block", agg.maxMetric, []schema.Point{
		{Val: 123.4, Ts: 120},
		{Val: 978894.445, Ts: 240},
	})
	compare("simple-cnt-skip-a-block", agg.cntMetric, []schema.Point{
		{Val: 2, Ts: 120},
		{Val: 3, Ts: 240},
	})
	compare("simple-lst-skip-a-block", agg.lstMetric, []schema.Point{
		{Val: 5, Ts: 120},
		{Val: 978894.445, Ts: 240},
	})
	compare("simple-sum-skip-a-block", agg.sumMetric, []schema.Point{
		{Val: 128.4, Ts: 120},
		{Val: 2451.123 + 1451.123 + 978894.445, Ts: 240},
	})

}
<commit_msg>New test case for Aggregator covering case when a point with an older timestamp is added<commit_after>package mdata

import (
	"testing"
	"time"

	"github.com/grafana/metrictank/cluster"
	"github.com/grafana/metrictank/conf"
	"github.com/grafana/metrictank/mdata/cache"
	"github.com/grafana/metrictank/test"
	"github.com/raintank/schema"
)

type testcase struct {
	ts       uint32
	span     uint32
	boundary uint32
}

func TestAggBoundary(t *testing.T) {
	cases := []testcase{
		{1, 120, 120},
		{50, 120, 120},
		{118, 120, 120},
		{119, 120, 120},
		{120, 120, 120},
		{121, 120, 240},
		{122, 120, 240},
		{150, 120, 240},
		{237, 120, 240},
		{238, 120, 240},
		{239, 120, 240},
		{240, 120, 240},
		{241, 120, 360},
	}
	for _, c := range cases {
		if ret := AggBoundary(c.ts, c.span); ret != c.boundary {
			t.Fatalf("aggBoundary for ts %d with span %d should be %d, not %d", c.ts, c.span, c.boundary, ret)
		}
	}
}

// note that values don't get "committed" to the metric until the aggregation interval is complete
func TestAggregator(t *testing.T) {
	cluster.Init("default", "test", time.Now(), "http", 6060)
	compare := func(key string, metric Metric, expected []schema.Point) {
		cluster.Manager.SetPrimary(true)
		res, err := metric.Get(0, 1000)

		if err != nil {
			t.Fatalf("expected err nil, got %v", err)
		}

		got := make([]schema.Point, 0, len(expected))
		for _, iter := range res.Iters {
			for iter.Next() {
				ts, val := iter.Values()
				got = append(got, schema.Point{Val: val, Ts: ts})
			}
		}
		if len(got) != len(expected) {
			t.Fatalf("output for testcase %s mismatch: expected: %v points, got: %v", key, len(expected), len(got))

		} else {
			for i, g := range got {
				exp := expected[i]
				if exp.Val != g.Val || exp.Ts != g.Ts {
					t.Fatalf("output for testcase %s mismatch at point %d: expected: %v, got: %v", key, i, exp, g)
				}
			}
		}
		cluster.Manager.SetPrimary(false)
	}
	ret := conf.NewRetentionMT(60, 86400, 120, 10, 0)
	aggs := conf.Aggregation{
		AggregationMethod: []conf.Method{conf.Avg, conf.Min, conf.Max, conf.Sum, conf.Lst},
	}

	agg := NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(0), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	expected := []schema.Point{}
	compare("simple-min-unfinished", agg.minMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(130, 130)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-one-block", agg.minMetric, expected)

	// points with a timestamp belonging to the previous aggregation are ignored
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(130, 130)
	agg.Add(90, 24)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-ignore-back-in-time", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 140 means points before chunk starting at 240 are discarded
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 140)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	// this point is not flushed to agg.minMetric because no point after it with a timestamp
	// crossing the aggregation boundary is added (aggregation span here is 60)
	agg.Add(130, 130)
	expected = []schema.Point{}
	compare("simple-min-ingest-from-all-before-next-chunk", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 115 means points before chunk starting at 120 are discarded
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 115)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	// this point is not flushed to agg.minMetric for the same reason as in the previous test
	agg.Add(130, 130)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-ingest-from-one-in-next-chunk", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 120 means points before chunk starting at 120 are discarded
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 120)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	// this point is not flushed to agg.minMetric for the same reason as in the previous test
	agg.Add(130, 130)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
	}
	compare("simple-min-ingest-from-on-chunk-boundary", agg.minMetric, expected)

	// chunkspan is 120, ingestFrom = 170 means points before chunk starting at 240 are discarded
	// but, each aggregation contains 60s of data, so the point at 240 covers raw data from 181..240

	// raw data         :   1..120 121..180 181..240 241..300 301..360
	// discarded data   :   xxxxxxxxxxxxxxxx
	// aggregated points:      120      180      240      300      360
	// chunks by t0     :      120               240      300      360
	// discarded chunk  :   xxxxxxxxxxxxxxxxxxxxx
	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(1), ret, aggs, false, 170)
	agg.Add(1, 1.1)
	agg.Add(119, 119)
	agg.Add(120, 120)
	agg.Add(121, 121)
	agg.Add(179, 179)
	agg.Add(180, 180)
	agg.Add(181, 181)
	agg.Add(220, 220)
	agg.Add(230, 230)
	agg.Add(231, 231)
	agg.Add(233, 233)
	agg.Add(239, 239)
	agg.Add(240, 240)
	agg.Add(245, 245)
	agg.Add(249, 249)
	agg.Add(250, 250)
	agg.Add(299, 299)
	agg.Add(300, 300)
	// these points are not flushed to agg.sumMetric for the same reason as in the previous test
	agg.Add(301, 0)
	agg.Add(320, 0)

	expected = []schema.Point{
		{Val: 181 + 220 + 230 + 231 + 233 + 239 + 240, Ts: 240},
		{Val: 245 + 249 + 250 + 299 + 300, Ts: 300},
	}
	compare("multi-sum-ingest-from-one-in-next-chunk", agg.sumMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(2), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(120, 4)
	expected = []schema.Point{
		{Val: 4, Ts: 120},
	}
	compare("simple-min-one-block-done-cause-last-point-just-right", agg.minMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(3), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(150, 1.123)
	agg.Add(180, 1)
	expected = []schema.Point{
		{Val: 5, Ts: 120},
		{Val: 1, Ts: 180},
	}
	compare("simple-min-two-blocks-done-cause-last-point-just-right", agg.minMetric, expected)

	agg = NewAggregator(mockstore, &cache.MockCache{}, test.GetAMKey(4), ret, aggs, false, 0)
	agg.Add(100, 123.4)
	agg.Add(110, 5)
	agg.Add(190, 2451.123)
	agg.Add(200, 1451.123)
	agg.Add(220, 978894.445)
	agg.Add(250, 1)
	compare("simple-min-skip-a-block", agg.minMetric, []schema.Point{
		{Val: 5, Ts: 120},
		{Val: 1451.123, Ts: 240},
	})
	compare("simple-max-skip-a-block", agg.maxMetric, []schema.Point{
		{Val: 123.4, Ts: 120},
		{Val: 978894.445, Ts: 240},
	})
	compare("simple-cnt-skip-a-block", agg.cntMetric, []schema.Point{
		{Val: 2, Ts: 120},
		{Val: 3, Ts: 240},
	})
	compare("simple-lst-skip-a-block", agg.lstMetric, []schema.Point{
		{Val: 5, Ts: 120},
		{Val: 978894.445, Ts: 240},
	})
	compare("simple-sum-skip-a-block", agg.sumMetric, []schema.Point{
		{Val: 128.4, Ts: 120},
		{Val: 2451.123 + 1451.123 + 978894.445, Ts: 240},
	})

}
<|endoftext|>


<commit_before>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// Params is an auxiliary structure to hold input parameters for setting the GA up
type Params struct {
	Nislands  int     // number of islands
	Ninds     int     // number of individuals: population size
	Nbases    int     // number of bases in chromosome
	Tf        int     // number of generations
	Dtout     int     // increment of time for output
	Dtmig     int     // increment of time for migration
	Pc        float64 // probability of crossover
	Pm        float64 // probability of mutation
	Elite     bool    // use elitism
	Rws       bool    // use Roulette-Wheel selection method
	Rnk       bool    // ranking
	RnkSP     float64 // selective pressure for ranking
	Fnkey     string  // key for reports' filenames
	UseIntRnd bool    // generate random integers instead of selecting from grid
	UseFltRnd bool    // generate random float point numbers instead of selecting from grid
	DoPlot    bool    // plot results
}

// ReadParams reads parameters from JSON file
func ReadParams(filenamepath string) *Params {

	// new params
	var o Params

	// set default values
	o.Nislands = 1
	o.Ninds = 10
	o.Nbases = 10
	o.Tf = 100
	o.Dtout = 10
	o.Dtmig = 40
	o.Pc = 0.8
	o.Pm = 0.01
	o.Elite = true
	o.Rws = false
	o.Rnk = true
	o.RnkSP = 1.2
	o.Fnkey = ""
	o.UseIntRnd = false
	o.UseFltRnd = false
	o.DoPlot = false

	// read file
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}

	// decode
	err = json.Unmarshal(b, &o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}

	// results
	return &o
}
<commit_msg>dtreg and nreg added to params<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// Params is an auxiliary structure to hold input parameters for setting the GA up
type Params struct {
	Nislands  int     // number of islands
	Ninds     int     // number of individuals: population size
	Nbases    int     // number of bases in chromosome
	Tf        int     // number of generations
	Dtout     int     // increment of time for output
	Dtmig     int     // increment of time for migration
	Dtreg     int     // increment of time for regeneration
	Nreg      int     // max number of generations
	Pc        float64 // probability of crossover
	Pm        float64 // probability of mutation
	Elite     bool    // use elitism
	Rws       bool    // use Roulette-Wheel selection method
	Rnk       bool    // ranking
	RnkSP     float64 // selective pressure for ranking
	Fnkey     string  // key for reports' filenames
	UseIntRnd bool    // generate random integers instead of selecting from grid
	UseFltRnd bool    // generate random float point numbers instead of selecting from grid
	DoPlot    bool    // plot results
}

// ReadParams reads parameters from JSON file
func ReadParams(filenamepath string) *Params {

	// new params
	var o Params

	// set default values
	o.Nislands = 1
	o.Ninds = 10
	o.Nbases = 10
	o.Tf = 100
	o.Dtout = 10
	o.Dtmig = 40
	o.Dtreg = 200
	o.Nreg = -1
	o.Pc = 0.8
	o.Pm = 0.01
	o.Elite = true
	o.Rws = false
	o.Rnk = true
	o.RnkSP = 1.2
	o.Fnkey = ""
	o.UseIntRnd = false
	o.UseFltRnd = false
	o.DoPlot = false

	// read file
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}

	// decode
	err = json.Unmarshal(b, &o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}

	// results
	return &o
}
<|endoftext|>


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

package router

import (
	"sort"
	"strings"
)

const (
	maxLevel = 255
)

type parser struct {
	fields map[uint8]records
	static map[string]Handle
}

type record struct {
	key    uint16
	handle Handle
	parts  []string
}

type records []*record

func (n records) Len() int           { return len(n) }
func (n records) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n records) Less(i, j int) bool { return n[i].key < n[j].key }

func newParser() *parser {
	return &parser{
		fields: make(map[uint8]records),
		static: make(map[string]Handle),
	}
}

func (p *parser) register(path string, handle Handle) bool {
	if parts, ok := split(path); ok {
		var static, dynamic uint16
		for _, value := range parts {
			if strings.HasPrefix(value, ":") {
				dynamic++
			} else {
				static++
			}
		}
		if dynamic == 0 {
			p.static["/"+strings.Join(parts, "/")] = handle
		} else {
			level := uint8(len(parts))
			p.fields[level] = append(p.fields[level], &record{key: dynamic<<8 + static, handle: handle, parts: parts})
			sort.Sort(records(p.fields[level]))
		}
		return true
	}

	return false
}

func (p *parser) get(path string) (handle Handle, result []Param, ok bool) {
	if handle, ok := p.static[path]; ok {
		return handle, nil, true
	}
	if parts, ok := split(path); ok {
		if handle, ok := p.static["/"+strings.Join(parts, "/")]; ok {
			return handle, nil, true
		}
		if data := p.fields[uint8(len(parts))]; data != nil {
			for _, nds := range data {
				values := nds.parts
				result = nil
				found := true
				for idx, value := range values {
					if value != parts[idx] && !(strings.HasPrefix(value, ":")) {
						found = false
						break
					} else {
						if strings.HasPrefix(value, ":") {
							result = append(result, Param{Key: value, Value: parts[idx]})
						}
					}
				}
				if found {
					return nds.handle, result, true
				}
			}
		}
	}

	return nil, nil, false
}

func split(path string) (result []string, ok bool) {
	sdata := strings.Split(strings.Trim(path, "/"), "/")
	if len(sdata) != 0 && len(sdata) < maxLevel {
		for _, value := range sdata {
			if v := strings.Trim(value, " "); v == "" {
				continue
			} else {
				result = append(result, v)
			}
		}
		if len(result) != 0 {
			ok = true
		}
	}

	return
}
<commit_msg>improve performance and memory usage<commit_after>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package router

import (
	"sort"
)

const (
	maxLevel = 255
)

type parser struct {
	fields map[uint8]records
	static map[string]Handle
}

type record struct {
	key    uint16
	handle Handle
	parts  []string
}

type records []*record

func (n records) Len() int           { return len(n) }
func (n records) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n records) Less(i, j int) bool { return n[i].key < n[j].key }

func newParser() *parser {
	return &parser{
		fields: make(map[uint8]records),
		static: make(map[string]Handle),
	}
}

func (p *parser) register(path string, handle Handle) bool {
	if parts, ok := split(path); ok {
		var static, dynamic uint16
		for _, value := range parts {
			if len(value) >= 1 && value[0:1] == ":" {
				dynamic++
			} else {
				static++
			}
		}
		if dynamic == 0 {
			p.static["/"+join(parts)] = handle
		} else {
			level := uint8(len(parts))
			p.fields[level] = append(p.fields[level], &record{key: dynamic<<8 + static, handle: handle, parts: parts})
			sort.Sort(records(p.fields[level]))
		}
		return true
	}

	return false
}

func (p *parser) get(path string) (handle Handle, result []Param, ok bool) {
	if handle, ok := p.static[path]; ok {
		return handle, nil, true
	}
	if parts, ok := split(path); ok {
		if handle, ok := p.static["/"+join(parts)]; ok {
			return handle, nil, true
		}
		if data := p.fields[uint8(len(parts))]; data != nil {
			for _, nds := range data {
				values := nds.parts
				result = nil
				found := true
				for idx, value := range values {
					if value != parts[idx] && !(len(value) >= 1 && value[0:1] == ":") {
						found = false
						break
					} else {
						if len(value) >= 1 && value[0:1] == ":" {
							result = append(result, Param{Key: value, Value: parts[idx]})
						}
					}
				}
				if found {
					return nds.handle, result, true
				}
			}
		}
	}

	return nil, nil, false
}

func split(path string) ([]string, bool) {
	sdata := explode(trim(path, "/"))
	if len(sdata) == 0 {
		return sdata, true
	}
	var result []string
	ind := 0
	if len(sdata) < maxLevel {
		result = make([]string, len(sdata))
		for _, value := range sdata {
			if v := trim(value, " "); v == "" {
				continue
			} else {
				result[ind] = v
				ind++
			}
		}
		return result[0:ind], true
	}

	return nil, false
}

func trim(str, sep string) string {
	result := str
	for {
		if len(result) >= 1 && result[0:1] == sep {
			result = result[1:]
		} else {
			break
		}
	}
	for {
		if len(result) >= 1 && result[len(result)-1:] == sep {
			result = result[:len(result)-1]
		} else {
			break
		}
	}
	return result
}

func join(parts []string) string {
	if len(parts) == 0 {
		return ""
	}
	if len(parts) == 1 {
		return parts[0]
	}
	n := len(parts) - 1
	for i := 0; i < len(parts); i++ {
		n += len(parts[i])
	}

	b := make([]byte, n)
	bp := copy(b, parts[0])
	for _, s := range parts[1:] {
		bp += copy(b[bp:], "/")
		bp += copy(b[bp:], s)
	}
	return string(b)
}

func explode(s string) []string {
	if len(s) == 0 {
		return []string{}
	}
	n := 1
	sep := "/"
	c := sep[0]
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			n++
		}
	}
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+1 <= len(s) && na+1 < n; i++ {
		if s[i] == c {
			a[na] = s[start:i]
			na++
			start = i + 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}
<|endoftext|>


<commit_before>package jwthelper

import (
	"fmt"

	"github.com/dgrijalva/jwt-go"
)

type Parser struct {
	key    interface{}
	parser jwt.Parser
}

type ParserOption struct {
	f func(p *Parser)
}

var (
	ErrInvalidParser = fmt.Errorf("invalid parser")
	ErrParseClaims   = fmt.Errorf("failed to parse claims")
	ErrInvalidToken  = fmt.Errorf("invalid token")
)

func ParserUseJSONNumber(flag bool) ParserOption {
	return ParserOption{func(p *Parser) {
		p.parser.UseJSONNumber = flag
	}}
}

func NewRSASHAParser(key []byte, options ...ParserOption) *Parser {
	p := &Parser{
		nil,
		jwt.Parser{
			// UseJSONNumber will call encoding/json.Decoder.UseNumber().
			// It causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
			// See https://godoc.org/encoding/json#Decoder.UseNumber
			UseJSONNumber: true,
			// If populated, only these methods will be considered valid
			// See https://godoc.org/github.com/dgrijalva/jwt-go#Parser
			ValidMethods: []string{
				jwt.SigningMethodRS256.Alg(),
				jwt.SigningMethodRS384.Alg(),
				jwt.SigningMethodRS512.Alg(),
			},
		},
	}

	// Override customized options.
	for _, op := range options {
		op.f(p)
	}

	publicKey, err := jwt.ParseRSAPublicKeyFromPEM(key)
	if err != nil {
		return &Parser{}
	}

	p.key = publicKey
	return p
}

func NewRSASHAParserFromPEMFile(publicPEM string, options ...ParserOption) *Parser {
	key, err := ReadKey(publicPEM)
	if err != nil {
		return &Parser{}
	}

	return NewRSASHAParser(key, options...)
}

func (p *Parser) Valid() bool {
	if p.key == nil {
		return false
	}
	return true
}

func (p *Parser) Parse(tokenString string) (map[string]interface{}, error) {
	m := map[string]interface{}{}

	if !p.Valid() {
		return m, ErrInvalidParser
	}

	token, err := p.parser.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return p.key, nil
	})

	if err != nil {
		return m, err
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return m, ErrParseClaims
	}

	if !token.Valid {
		return m, ErrInvalidToken
	}

	return claims, nil
}
<commit_msg>Add comments for Parser, ParserOption, Err types<commit_after>package jwthelper

import (
	"fmt"

	"github.com/dgrijalva/jwt-go"
)

// Parser is used to parse JWT token string.
type Parser struct {
	key    interface{}
	parser jwt.Parser
}

// ParserOption represents the option for parsing JWT token string.
// Use option helper functions to set options:
// e.g. ParserUseJSONNumber()
type ParserOption struct {
	f func(p *Parser)
}

var (
	// ErrInvalidParser represents the error of invalid parser.
	ErrInvalidParser = fmt.Errorf("invalid parser")
	// ErrParseClaims represents the error of failed to parse claims.
	ErrParseClaims = fmt.Errorf("failed to parse claims")
	// ErrInvalidToken represents the error of invalid token.
	ErrInvalidToken = fmt.Errorf("invalid token")
)

func ParserUseJSONNumber(flag bool) ParserOption {
	return ParserOption{func(p *Parser) {
		p.parser.UseJSONNumber = flag
	}}
}

func NewRSASHAParser(key []byte, options ...ParserOption) *Parser {
	p := &Parser{
		nil,
		jwt.Parser{
			// UseJSONNumber will call encoding/json.Decoder.UseNumber().
			// It causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
			// See https://godoc.org/encoding/json#Decoder.UseNumber
			UseJSONNumber: true,
			// If populated, only these methods will be considered valid
			// See https://godoc.org/github.com/dgrijalva/jwt-go#Parser
			ValidMethods: []string{
				jwt.SigningMethodRS256.Alg(),
				jwt.SigningMethodRS384.Alg(),
				jwt.SigningMethodRS512.Alg(),
			},
		},
	}

	// Override customized options.
	for _, op := range options {
		op.f(p)
	}

	publicKey, err := jwt.ParseRSAPublicKeyFromPEM(key)
	if err != nil {
		return &Parser{}
	}

	p.key = publicKey
	return p
}

func NewRSASHAParserFromPEMFile(publicPEM string, options ...ParserOption) *Parser {
	key, err := ReadKey(publicPEM)
	if err != nil {
		return &Parser{}
	}

	return NewRSASHAParser(key, options...)
}

func (p *Parser) Valid() bool {
	if p.key == nil {
		return false
	}
	return true
}

func (p *Parser) Parse(tokenString string) (map[string]interface{}, error) {
	m := map[string]interface{}{}

	if !p.Valid() {
		return m, ErrInvalidParser
	}

	token, err := p.parser.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return p.key, nil
	})

	if err != nil {
		return m, err
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return m, ErrParseClaims
	}

	if !token.Valid {
		return m, ErrInvalidToken
	}

	return claims, nil
}
<|endoftext|>


<commit_before>package css

import (
	"fmt"
	"github.com/gorilla/css/scanner"
	"strings"
)

type State int

const (
	STATE_NONE State = iota
	STATE_SELECTOR
	STATE_DECLARE_BLOCK
	STATE_PROPERTY
	STATE_VALUE
)

type ParserContext struct {
	State           State
	NowSelectorText string
	NowProperty     string
	NowValue        string
	NowImportant    int
	NowRuleType     RuleType

	CurrentRule *CSSRule
}

func Parse(csstext string) *CSSStyleSheet {
	context := &ParserContext{
		State:           STATE_NONE,
		NowSelectorText: "",
		NowProperty:     "",
		NowValue:        "",
		NowImportant:    0,
		NowRuleType:     STYLE_RULE,
	}

	css := &CSSStyleSheet{}
	css.CssRuleList = make([]*CSSRule, 0)
	s := scanner.New(csstext)

	for {
		token := s.Next()

		fmt.Printf("%s:'%s'\n", token.Type.String(), token.Value)

		if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
			break
		}

		switch token.Type {
		case scanner.TokenAtKeyword:
			context.State = STATE_SELECTOR
			switch token.Value {
			case "@media":
				context.NowRuleType = MEDIA_RULE
			}

		case scanner.TokenString:
			if context.State == STATE_SELECTOR {
				context.NowSelectorText += token.Value
			}
		case scanner.TokenURI:
		case scanner.TokenUnicodeRange:
		case scanner.TokenCDO:
		case scanner.TokenCDC:
		case scanner.TokenComment:
		case scanner.TokenIdent:

			if context.State == STATE_NONE || context.State == STATE_SELECTOR {
				context.State = STATE_SELECTOR
				context.NowSelectorText += strings.TrimSpace(token.Value)
				break
			}

			if context.State == STATE_DECLARE_BLOCK {
				context.State = STATE_PROPERTY
				context.NowProperty = strings.TrimSpace(token.Value)
				break
			}

			if context.State == STATE_VALUE {
				if token.Value == "important" {
					context.NowImportant = 1
				} else {
					context.NowValue = token.Value + " "
				}
				break
			}

		case scanner.TokenDimension:
			fallthrough
		case scanner.TokenS:
			if context.State == STATE_SELECTOR {
				context.NowSelectorText += token.Value
			} else if context.State == STATE_VALUE {
				context.NowValue += token.Value
			}

		case scanner.TokenChar:
			if context.State == STATE_NONE {
				if "." == token.Value {
					context.State = STATE_SELECTOR
					context.NowSelectorText += token.Value
					break
				}
			}
			if context.State == STATE_SELECTOR {
				if "{" == token.Value {
					context.State = STATE_DECLARE_BLOCK
					context.CurrentRule = NewRule(context.NowRuleType)
					context.CurrentRule.Style.SelectorText = strings.TrimSpace(context.NowSelectorText)
					break
				} else {
					context.NowSelectorText += token.Value
				}
				break
			}

			if context.State == STATE_PROPERTY {
				if token.Value == ":" {
					context.State = STATE_VALUE
				}
				break
			}

			if context.State == STATE_DECLARE_BLOCK {
				if token.Value == "}" {
					css.CssRuleList = append(css.CssRuleList, context.CurrentRule)
					context.NowSelectorText = ""
					context.NowProperty = ""
					context.NowValue = ""
					context.NowImportant = 0
					context.NowRuleType = STYLE_RULE
					context.State = STATE_NONE
				}
				break
			}

			if context.State == STATE_VALUE {
				if token.Value == ";" {
					decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
					context.CurrentRule.Style.Styles[context.NowProperty] = decl

					context.NowProperty = ""
					context.NowValue = ""
					context.NowImportant = 0
					context.State = STATE_DECLARE_BLOCK
				} else if token.Value == "}" { // last property in a block can have optional ;
					decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
					context.CurrentRule.Style.Styles[context.NowProperty] = decl
					css.CssRuleList = append(css.CssRuleList, context.CurrentRule)
					context.NowSelectorText = ""
					context.NowProperty = ""
					context.NowValue = ""
					context.NowImportant = 0
					context.NowRuleType = STYLE_RULE
					context.State = STATE_NONE
				} else if token.Value != "!" {
						context.NowValue += token.Value
				}
				break

			}
		case scanner.TokenPercentage:
			fallthrough
		case scanner.TokenHash:
			if context.State == STATE_NONE || context.State == STATE_SELECTOR {
				context.State = STATE_SELECTOR
				context.NowSelectorText += strings.TrimSpace(token.Value)
				break
			}

			fallthrough
		case scanner.TokenNumber:
			fallthrough
		case scanner.TokenFunction:
			fallthrough
		case scanner.TokenIncludes:
			fallthrough
		case scanner.TokenDashMatch:
			fallthrough
		case scanner.TokenPrefixMatch:
			fallthrough
		case scanner.TokenSuffixMatch:
			fallthrough
		case scanner.TokenSubstringMatch:
			if context.State == STATE_VALUE {
				context.NowValue += token.Value
			}
		}
	}
	return css
}
<commit_msg>Log default so we can see if we miss any type. Good during this early state<commit_after>package css

import (
	"fmt"
	"github.com/gorilla/css/scanner"
	"strings"
)

type State int

const (
	STATE_NONE State = iota
	STATE_SELECTOR
	STATE_DECLARE_BLOCK
	STATE_PROPERTY
	STATE_VALUE
)

type ParserContext struct {
	State           State
	NowSelectorText string
	NowProperty     string
	NowValue        string
	NowImportant    int
	NowRuleType     RuleType

	CurrentRule *CSSRule
}

func Parse(csstext string) *CSSStyleSheet {
	context := &ParserContext{
		State:           STATE_NONE,
		NowSelectorText: "",
		NowProperty:     "",
		NowValue:        "",
		NowImportant:    0,
		NowRuleType:     STYLE_RULE,
	}

	css := &CSSStyleSheet{}
	css.CssRuleList = make([]*CSSRule, 0)
	s := scanner.New(csstext)

	for {
		token := s.Next()

		fmt.Printf("%s:'%s'\n", token.Type.String(), token.Value)

		if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
			break
		}

		switch token.Type {
		case scanner.TokenAtKeyword:
			context.State = STATE_SELECTOR
			switch token.Value {
			case "@media":
				context.NowRuleType = MEDIA_RULE
			}

		case scanner.TokenString:
			if context.State == STATE_SELECTOR {
				context.NowSelectorText += token.Value
			}
		case scanner.TokenURI:
		case scanner.TokenUnicodeRange:
		case scanner.TokenCDO:
		case scanner.TokenCDC:
		case scanner.TokenComment:
		case scanner.TokenIdent:

			if context.State == STATE_NONE || context.State == STATE_SELECTOR {
				context.State = STATE_SELECTOR
				context.NowSelectorText += strings.TrimSpace(token.Value)
				break
			}

			if context.State == STATE_DECLARE_BLOCK {
				context.State = STATE_PROPERTY
				context.NowProperty = strings.TrimSpace(token.Value)
				break
			}

			if context.State == STATE_VALUE {
				if token.Value == "important" {
					context.NowImportant = 1
				} else {
					context.NowValue = token.Value + " "
				}
				break
			}

		case scanner.TokenDimension:
			fallthrough
		case scanner.TokenS:
			if context.State == STATE_SELECTOR {
				context.NowSelectorText += token.Value
			} else if context.State == STATE_VALUE {
				context.NowValue += token.Value
			}

		case scanner.TokenChar:
			if context.State == STATE_NONE {
				if "." == token.Value {
					context.State = STATE_SELECTOR
					context.NowSelectorText += token.Value
					break
				}
			}
			if context.State == STATE_SELECTOR {
				if "{" == token.Value {
					context.State = STATE_DECLARE_BLOCK
					context.CurrentRule = NewRule(context.NowRuleType)
					context.CurrentRule.Style.SelectorText = strings.TrimSpace(context.NowSelectorText)
					break
				} else {
					context.NowSelectorText += token.Value
				}
				break
			}

			if context.State == STATE_PROPERTY {
				if token.Value == ":" {
					context.State = STATE_VALUE
				}
				break
			}

			if context.State == STATE_DECLARE_BLOCK {
				if token.Value == "}" {
					css.CssRuleList = append(css.CssRuleList, context.CurrentRule)
					context.NowSelectorText = ""
					context.NowProperty = ""
					context.NowValue = ""
					context.NowImportant = 0
					context.NowRuleType = STYLE_RULE
					context.State = STATE_NONE
				}
				break
			}

			if context.State == STATE_VALUE {
				if token.Value == ";" {
					decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
					context.CurrentRule.Style.Styles[context.NowProperty] = decl

					context.NowProperty = ""
					context.NowValue = ""
					context.NowImportant = 0
					context.State = STATE_DECLARE_BLOCK
				} else if token.Value == "}" { // last property in a block can have optional ;
					decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
					context.CurrentRule.Style.Styles[context.NowProperty] = decl
					css.CssRuleList = append(css.CssRuleList, context.CurrentRule)
					context.NowSelectorText = ""
					context.NowProperty = ""
					context.NowValue = ""
					context.NowImportant = 0
					context.NowRuleType = STYLE_RULE
					context.State = STATE_NONE
				} else if token.Value != "!" {
					context.NowValue += token.Value
				}
				break

			}
		case scanner.TokenPercentage:
			fallthrough
		case scanner.TokenHash:
			if context.State == STATE_NONE || context.State == STATE_SELECTOR {
				context.State = STATE_SELECTOR
				context.NowSelectorText += strings.TrimSpace(token.Value)
				break
			}

			fallthrough
		case scanner.TokenNumber:
			fallthrough
		case scanner.TokenFunction:
			fallthrough
		case scanner.TokenIncludes:
			fallthrough
		case scanner.TokenDashMatch:
			fallthrough
		case scanner.TokenPrefixMatch:
			fallthrough
		case scanner.TokenSuffixMatch:
			fallthrough
		case scanner.TokenSubstringMatch:
			if context.State == STATE_VALUE {
				context.NowValue += token.Value
			}
		default:
			fmt.Printf("Unhandled, %s:'%s'\n", token.Type.String(), token.Value)
		}
	}
	return css
}
<|endoftext|>


<commit_before>package astgen

import (
	"strconv"
)

// TODO: Get rid of panics.

type parser struct {
	file []byte
}

func Load(data []byte) (def *LangDef, e error) {
	var p parser
	p.file = data

	def = new(LangDef)
	def.Types = make(map[string]Type)

	for !p.finished() {
		t := p.parse_type()
		def.Types[t.Common().Name] = t
	}

	return def, nil
}

func is_space(b byte) bool {
	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

func is_letter(b byte) bool {
	return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_'
}

func (p *parser) accept_token(token string) bool {
	for is_space(p.file[0]) {
		p.file = p.file[1:]
	}

	tok := []byte(token)

	if len(p.file) < len(tok) {
		return false
	}

	for i := range tok {
		if p.file[i] != tok[i] {
			return false
		}
	}

	if len(p.file) != len(tok) {
		next := p.file[len(tok)]
		if !is_space(next) && is_letter(p.file[len(tok)-1]) == is_letter(next) {
			return false
		}
	}

	p.file = p.file[len(tok):]

	return true
}

func (p *parser) match_token(token string) {
	if !p.accept_token(token) {
		panic("can't match token \"" + token + "\". Current: \"" + p.current_token() + "\"")
	}
}

func (p *parser) consume_line() string {
	for is_space(p.file[0]) {
		p.file = p.file[1:]
	}

	for i := range p.file {
		if p.file[i] == '\n' {
			ret := string(p.file[:i])
			p.file = p.file[i+1:]
			return ret
		}
	}

	ret := string(p.file)
	p.file = nil
	return ret
}

func (p *parser) consume_token() string {
	for is_space(p.file[0]) {
		p.file = p.file[1:]
	}

	if len(p.file) == 0 {
		panic("bad file")
	}

	ident := is_letter(p.file[0])

	for i := range p.file {
		if ident != is_letter(p.file[i]) || is_space(p.file[i]) {
			ret := string(p.file[:i])
			p.file = p.file[i:]
			return ret
		}
	}

	ret := string(p.file)
	p.file = nil
	return ret
}

func (p *parser) current_token() string {
	for is_space(p.file[0]) {
		p.file = p.file[1:]
	}

	if len(p.file) == 0 {
		panic("bad file")
	}

	ident := is_letter(p.file[0])

	for i := range p.file {
		if ident != is_letter(p.file[i]) || is_space(p.file[i]) {
			return string(p.file[:i])
		}
	}

	return string(p.file)
}

func (p *parser) finished() bool {
	for len(p.file) > 0 && is_space(p.file[0]) {
		p.file = p.file[1:]
	}
	return len(p.file) == 0
}

func (p *parser) parse_production() Production {
	var line []byte

	for i := range p.file {
		if p.file[i] == '\n' {
			line = p.file[:i]
			p.file = p.file[i:]
			break
		}
	}

	if line == nil {
		panic("bad file")
	}

	tokens := []Token{}

	for len(line) != 0 {
		for len(line) != 0 && line[0] == ' ' {
			line = line[1:]
		}

		token_len := len(line)

		for i := range line {
			if line[i] == ' ' {
				token_len = i
				break
			}
		}

		var token Token
		if line[0] == '$' {
			token = Token{string(line[1:token_len]), ""}
		} else {
			token = Token{"", string(line[:token_len])}
		}
		tokens = append(tokens, token)
		line = line[token_len:]
	}

	return Production{tokens}
}

func (p *parser) parse_member() (m StructMember) {
	for p.accept_token("//") {
		if p.accept_token("terminator") {
			pr := p.parse_production()
			m.ArrayTerminator = &pr
		} else if p.accept_token("separator") {
			pr := p.parse_production()
			m.ArraySeparator = &pr
		} else if p.accept_token("min_length") {
			var err error
			m.ArrayMinLength, err = strconv.Atoi(string(p.consume_line()))
			if err != nil {
				panic(err)
			}
		} else {
			panic("bad file")
		}
	}

	m.Name = p.consume_token()
	p.match_token(":")

	if p.accept_token("?") {
		m.Nullable = true
	}
	if p.accept_token("[]") {
		m.Array = true
	}

	m.Type = p.consume_token()
	return
}

func (p *parser) parse_struct_type() *StructType {
	typ := new(StructType)

	for p.accept_token("//") {
		typ.Productions = append(typ.Productions, p.parse_production())
	}

	p.match_token("type")

	typ.Name = p.consume_token()

	p.match_token("=")
	p.match_token("struct")
	p.match_token("{")

	for !p.accept_token("}") {
		typ.Members = append(typ.Members, p.parse_member())
	}

	return typ
}

func (p *parser) parse_struct_type2(name string) *StructType {
	typ := new(StructType)
	typ.Name = name

	p.match_token("struct")
	p.match_token("{")

	for !p.accept_token("}") {
		typ.Members = append(typ.Members, p.parse_member())
	}

	return typ
}

func (p *parser) parse_type() Type {
	if p.current_token() == "//" {
		return p.parse_struct_type()
	}

	p.match_token("type")

	name := p.consume_token()

	if !p.accept_token("=") {
		return &LexicalType{TypeBase{name, false}}
	}

	if p.current_token() == "struct" {
		return p.parse_struct_type2(name)
	}

	typ := new(OptionType)
	typ.Name = name
	typ.Options = []string{p.consume_token()}

	for p.accept_token("|") {
		typ.Options = append(typ.Options, p.consume_token())
	}

	return typ
}
<commit_msg>Improve error reporting.<commit_after>package astgen

import (
	"strconv"
	"fmt"
)

type parser struct {
	file []byte
	line int
}

func Load(data []byte) (def *LangDef, e error) {
	var p parser
	p.line = 1
	p.file = data

	defer func() {
		e := recover()
		if e != nil {
			def = nil
			e = fmt.Errorf("Error on line %d: %s\n", p.line, e)
		}
	}()

	def = new(LangDef)
	def.Types = make(map[string]Type)

	for !p.finished() {
		t := p.parse_type()
		def.Types[t.Common().Name] = t
	}

	return def, nil
}

func is_nl(b byte) bool {
	return b == '\n' || b == '\r'
}

func is_space(b byte) bool {
	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

func is_letter(b byte) bool {
	return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_'
}

func (p *parser) skip_space() {
	for len(p.file) > 0 && is_space(p.file[0]) {
		if is_nl(p.file[0]) {
			p.line++
		}

		p.file = p.file[1:]
	}
}

func (p *parser) accept_token(token string) bool {
	p.skip_space()

	tok := []byte(token)

	if len(p.file) < len(tok) {
		return false
	}

	for i := range tok {
		if p.file[i] != tok[i] {
			return false
		}
	}

	if len(p.file) != len(tok) {
		next := p.file[len(tok)]
		if !is_space(next) && is_letter(p.file[len(tok)-1]) == is_letter(next) {
			return false
		}
	}

	p.file = p.file[len(tok):]

	return true
}

func (p *parser) match_token(token string) {
	if !p.accept_token(token) {
		panic("can't match token \"" + token + "\". Current: \"" + p.current_token() + "\"")
	}
}

func (p *parser) consume_line() string {
	p.skip_space()

	for i := range p.file {
		if p.file[i] == '\n' {
			ret := string(p.file[:i])
			p.file = p.file[i+1:]
			p.line++
			return ret
		}
	}

	ret := string(p.file)
	p.file = nil
	return ret
}

func (p *parser) consume_token() string {
	p.skip_space()

	if len(p.file) == 0 {
		panic("bad file")
	}

	ident := is_letter(p.file[0])

	for i := range p.file {
		if ident != is_letter(p.file[i]) || is_space(p.file[i]) {
			ret := string(p.file[:i])
			p.file = p.file[i:]
			return ret
		}
	}

	ret := string(p.file)
	p.file = nil
	return ret
}

func (p *parser) current_token() string {
	p.skip_space()

	if len(p.file) == 0 {
		panic("bad file")
	}

	ident := is_letter(p.file[0])

	for i := range p.file {
		if ident != is_letter(p.file[i]) || is_space(p.file[i]) {
			return string(p.file[:i])
		}
	}

	return string(p.file)
}

func (p *parser) finished() bool {
	p.skip_space()
	return len(p.file) == 0
}

func (p *parser) parse_production() Production {
	var line []byte

	for i := range p.file {
		if p.file[i] == '\n' {
			p.line++
			line = p.file[:i]
			p.file = p.file[i:]
			break
		}
	}

	if line == nil {
		panic("bad file")
	}

	tokens := []Token{}

	for len(line) != 0 {
		for len(line) != 0 && line[0] == ' ' {
			line = line[1:]
		}

		token_len := len(line)

		for i := range line {
			if line[i] == ' ' {
				token_len = i
				break
			}
		}

		var token Token
		if line[0] == '$' {
			token = Token{string(line[1:token_len]), ""}
		} else {
			token = Token{"", string(line[:token_len])}
		}
		tokens = append(tokens, token)
		line = line[token_len:]
	}

	return Production{tokens}
}

func (p *parser) parse_member() (m StructMember) {
	for p.accept_token("//") {
		if p.accept_token("terminator") {
			pr := p.parse_production()
			m.ArrayTerminator = &pr
		} else if p.accept_token("separator") {
			pr := p.parse_production()
			m.ArraySeparator = &pr
		} else if p.accept_token("min_length") {
			var err error
			m.ArrayMinLength, err = strconv.Atoi(string(p.consume_line()))
			if err != nil {
				panic(err)
			}
		} else {
			panic("bad file")
		}
	}

	m.Name = p.consume_token()
	p.match_token(":")

	if p.accept_token("?") {
		m.Nullable = true
	}
	if p.accept_token("[]") {
		m.Array = true
	}

	m.Type = p.consume_token()
	return
}

func (p *parser) parse_struct_type() *StructType {
	typ := new(StructType)

	for p.accept_token("//") {
		typ.Productions = append(typ.Productions, p.parse_production())
	}

	p.match_token("type")

	typ.Name = p.consume_token()

	p.match_token("=")
	p.match_token("struct")
	p.match_token("{")

	for !p.accept_token("}") {
		typ.Members = append(typ.Members, p.parse_member())
	}

	return typ
}

func (p *parser) parse_struct_type2(name string) *StructType {
	typ := new(StructType)
	typ.Name = name

	p.match_token("struct")
	p.match_token("{")

	for !p.accept_token("}") {
		typ.Members = append(typ.Members, p.parse_member())
	}

	return typ
}

func (p *parser) parse_type() Type {
	if p.current_token() == "//" {
		return p.parse_struct_type()
	}

	p.match_token("type")

	name := p.consume_token()

	if !p.accept_token("=") {
		return &LexicalType{TypeBase{name, false}}
	}

	if p.current_token() == "struct" {
		return p.parse_struct_type2(name)
	}

	typ := new(OptionType)
	typ.Name = name
	typ.Options = []string{p.consume_token()}

	for p.accept_token("|") {
		typ.Options = append(typ.Options, p.consume_token())
	}

	return typ
}
<|endoftext|>


<commit_before>// Copyright 2011 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 multipart

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

func TestWriter(t *testing.T) {
	fileContents := []byte("my file contents")

	var b bytes.Buffer
	w := NewWriter(&b)
	{
		part, err := w.CreateFormFile("myfile", "my-file.txt")
		if err != nil {
			t.Fatalf("CreateFormFile: %v", err)
		}
		part.Write(fileContents)
		err = w.WriteField("key", "val")
		if err != nil {
			t.Fatalf("WriteField: %v", err)
		}
		part.Write([]byte("val"))
		err = w.Close()
		if err != nil {
			t.Fatalf("Close: %v", err)
		}
		s := b.String()
		if len(s) == 0 {
			t.Fatal("String: unexpected empty result")
		}
		if s[0] == '\r' || s[0] == '\n' {
			t.Fatal("String: unexpected newline")
		}
	}

	r := NewReader(&b, w.Boundary())

	part, err := r.NextPart()
	if err != nil {
		t.Fatalf("part 1: %v", err)
	}
	if g, e := part.FormName(), "myfile"; g != e {
		t.Errorf("part 1: want form name %q, got %q", e, g)
	}
	slurp, err := ioutil.ReadAll(part)
	if err != nil {
		t.Fatalf("part 1: ReadAll: %v", err)
	}
	if e, g := string(fileContents), string(slurp); e != g {
		t.Errorf("part 1: want contents %q, got %q", e, g)
	}

	part, err = r.NextPart()
	if err != nil {
		t.Fatalf("part 2: %v", err)
	}
	if g, e := part.FormName(), "key"; g != e {
		t.Errorf("part 2: want form name %q, got %q", e, g)
	}
	slurp, err = ioutil.ReadAll(part)
	if err != nil {
		t.Fatalf("part 2: ReadAll: %v", err)
	}
	if e, g := "val", string(slurp); e != g {
		t.Errorf("part 2: want contents %q, got %q", e, g)
	}

	part, err = r.NextPart()
	if part != nil || err == nil {
		t.Fatalf("expected end of parts; got %v, %v", part, err)
	}
}

func TestWriterSetBoundary(t *testing.T) {
	var b bytes.Buffer
	w := NewWriter(&b)
	tests := []struct {
		b  string
		ok bool
	}{
		{"abc", true},
		{"", false},
		{"ungültig", false},
		{"!", false},
		{strings.Repeat("x", 69), true},
		{strings.Repeat("x", 70), false},
		{"bad!ascii!", false},
		{"my-separator", true},
	}
	for i, tt := range tests {
		err := w.SetBoundary(tt.b)
		got := err == nil
		if got != tt.ok {
			t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
		} else if tt.ok {
			got := w.Boundary()
			if got != tt.b {
				t.Errorf("boundary = %q; want %q", got, tt.b)
			}
		}
	}
	w.Close()
	if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") {
		t.Errorf("expected my-separator in output. got: %q", got)
	}
}

func TestWriterBoundaryGoroutines(t *testing.T) {
	// Verify there's no data race accessing any lazy boundary if it's used by
	// different goroutines. This was previously broken by
	// https://codereview.appspot.com/95760043/ and reverted in
	// https://codereview.appspot.com/117600043/
	w := NewWriter(ioutil.Discard)
	go func() {
		w.CreateFormField("foo")
	}()
	w.Boundary()
}
<commit_msg>mime/multipart: fix Writer data race test<commit_after>// Copyright 2011 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 multipart

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

func TestWriter(t *testing.T) {
	fileContents := []byte("my file contents")

	var b bytes.Buffer
	w := NewWriter(&b)
	{
		part, err := w.CreateFormFile("myfile", "my-file.txt")
		if err != nil {
			t.Fatalf("CreateFormFile: %v", err)
		}
		part.Write(fileContents)
		err = w.WriteField("key", "val")
		if err != nil {
			t.Fatalf("WriteField: %v", err)
		}
		part.Write([]byte("val"))
		err = w.Close()
		if err != nil {
			t.Fatalf("Close: %v", err)
		}
		s := b.String()
		if len(s) == 0 {
			t.Fatal("String: unexpected empty result")
		}
		if s[0] == '\r' || s[0] == '\n' {
			t.Fatal("String: unexpected newline")
		}
	}

	r := NewReader(&b, w.Boundary())

	part, err := r.NextPart()
	if err != nil {
		t.Fatalf("part 1: %v", err)
	}
	if g, e := part.FormName(), "myfile"; g != e {
		t.Errorf("part 1: want form name %q, got %q", e, g)
	}
	slurp, err := ioutil.ReadAll(part)
	if err != nil {
		t.Fatalf("part 1: ReadAll: %v", err)
	}
	if e, g := string(fileContents), string(slurp); e != g {
		t.Errorf("part 1: want contents %q, got %q", e, g)
	}

	part, err = r.NextPart()
	if err != nil {
		t.Fatalf("part 2: %v", err)
	}
	if g, e := part.FormName(), "key"; g != e {
		t.Errorf("part 2: want form name %q, got %q", e, g)
	}
	slurp, err = ioutil.ReadAll(part)
	if err != nil {
		t.Fatalf("part 2: ReadAll: %v", err)
	}
	if e, g := "val", string(slurp); e != g {
		t.Errorf("part 2: want contents %q, got %q", e, g)
	}

	part, err = r.NextPart()
	if part != nil || err == nil {
		t.Fatalf("expected end of parts; got %v, %v", part, err)
	}
}

func TestWriterSetBoundary(t *testing.T) {
	var b bytes.Buffer
	w := NewWriter(&b)
	tests := []struct {
		b  string
		ok bool
	}{
		{"abc", true},
		{"", false},
		{"ungültig", false},
		{"!", false},
		{strings.Repeat("x", 69), true},
		{strings.Repeat("x", 70), false},
		{"bad!ascii!", false},
		{"my-separator", true},
	}
	for i, tt := range tests {
		err := w.SetBoundary(tt.b)
		got := err == nil
		if got != tt.ok {
			t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
		} else if tt.ok {
			got := w.Boundary()
			if got != tt.b {
				t.Errorf("boundary = %q; want %q", got, tt.b)
			}
		}
	}
	w.Close()
	if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") {
		t.Errorf("expected my-separator in output. got: %q", got)
	}
}

func TestWriterBoundaryGoroutines(t *testing.T) {
	// Verify there's no data race accessing any lazy boundary if it's used by
	// different goroutines. This was previously broken by
	// https://codereview.appspot.com/95760043/ and reverted in
	// https://codereview.appspot.com/117600043/
	w := NewWriter(ioutil.Discard)
	done := make(chan int)
	go func() {
		w.CreateFormField("foo")
		done <- 1
	}()
	w.Boundary()
	<-done
}
<|endoftext|>


<commit_before>package jsonparser

import (
	"bytes"
	"errors"
	"fmt"
	tm "github.com/buger/goterm"
	d "runtime/debug"
	"strconv"
)

var debug = false

// Find position of next character which is not ' ', ',', '}' or ']'
func nextValue(data []byte) (offset int) {
	for true {
		if len(data) == offset {
			return -1
		}

		if data[offset] != ' ' && data[offset] != '\n' && data[offset] != ',' && data[offset] != '}' && data[offset] != ']' {
			return
		}

		offset += 1
	}

	return -1
}

// Tries to find the end of string
// Support if string contains escaped quote symbols.
func stringEnd(data []byte) int {
	i := 0

	for true {
		sIdx := bytes.IndexByte(data[i:], '"')

		if sIdx == -1 {
			return -1
		}

		i += sIdx + 1

		// If it just escaped \", continue
		if i > 2 && data[i-2] == '\\' {
			continue
		}

		break
	}

	return i
}

// Find end of the data structure, array or object.
// For array openSym and closeSym will be '[' and ']', for object '{' and '}'
// Know about nested structures
func trailingBracket(data []byte, openSym byte, closeSym byte) int {
	level := 0
	i := 0
	ln := len(data)

	for true {
		if i >= ln {
			if debug {
				fmt.Println("can't find matching bracket", level, tm.Highlight(tm.Highlight(tm.Context(string(data), i, 500), string(openSym), tm.RED), string(closeSym), tm.BLUE))
			}
			return -1
		}

		c := data[i]

		// If inside string, skip it
		if c == '"' {
			//sFrom := i
			i++

			se := stringEnd(data[i:])
			if se == -1 {
				return -1
			}
			i += se - 1
			//fmt.Println("Found string:", level, tm.HighlightRegion(data, sFrom, i, tm.GREEN))
		}

		if c == openSym {
			level += 1

			if debug {
				fmt.Println("Found open sym:", level, tm.HighlightRegion(string(data), i, i+1, tm.RED))
			}
		} else if c == closeSym {
			level -= 1

			if debug {
				fmt.Println("Found close sym:", level, tm.HighlightRegion(string(data), i, i+1, tm.BLUE))
			}
		}

		i++

		if level == 0 {
			break
		}
	}

	if debug {
		fmt.Println("Found matching brackets:", tm.HighlightRegion(string(data), 0, i, tm.YELLOW))
	}

	return i
}

const (
	NOT_EXIST = iota
	STRING
	NUMBER
	OBJECT
	ARRAY
	BOOLEAN
	NULL
)

/*
Get - Receives data structure, and key path to extract value from.

Returns:
`value` - Pointer to original data structure containing key value, or just empty slice if nothing found or error
`dataType` -    Can be: `NOT_EXIST`, `STRING`, `NUMBER`, `OBJECT`, `ARRAY`, `BOOLEAN` or `NULL`
`offset` - Offset from provided data structure where key value ends. Used mostly internally, for example for `ArrayEach` helper.
`err` - If key not found or any other parsing issue it should return error. If key not found it also sets `dataType` to `NOT_EXISTS`

Accept multiple keys to specify path to JSON value (in case of quering nested structures).
If no keys provided it will try to extract closest JSON value (simple ones or object/array), useful for reading streams or arrays, see `ArrayEach` implementation.
*/
func Get(data []byte, keys ...string) (value []byte, dataType int, offset int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("Unhandler JSON parsing error: %v, %s", r, string(d.Stack())))
		}
	}()

	ln := len(data)

	if len(keys) > 0 {
		for _, k := range keys {
			lk := len(k)

			for true {
				if idx := bytes.Index(data[offset:], []byte(k)); idx != -1 && (ln-(offset+idx+lk+2)) > 0 {
					offset += idx

					if data[offset+lk] == '"' && data[offset-1] == '"' && data[offset+lk+1] == ':' {
						offset += lk + 2
						nO := nextValue(data[offset:])

						if nO == -1 {
							return []byte{}, NOT_EXIST, -1, errors.New("Malformed JSON error")
						}

						offset += nO

						break
					} else {
						offset += 1
					}
				} else {
					return []byte{}, NOT_EXIST, -1, errors.New("Key path not found")
				}
			}
		}
	} else {
		nO := nextValue(data[offset:])

		if nO == -1 {
			return []byte{}, NOT_EXIST, -1, errors.New("Malformed JSON error")
		}

		offset = nO
	}

	endOffset := offset

	// if string value
	if data[offset] == '"' {
		dataType = STRING
		if idx := stringEnd(data[offset+1:]); idx != -1 {
			endOffset += idx + 1
		} else {
			return []byte{}, dataType, offset, errors.New("Value is string, but can't find closing '\"' symbol")
		}
	} else if data[offset] == '[' { // if array value
		dataType = ARRAY
		// break label, for stopping nested loops
		endOffset = trailingBracket(data[offset:], '[', ']')

		if endOffset == -1 {
			return []byte{}, dataType, offset, errors.New("Value is array, but can't find closing ']' symbol")
		}

		endOffset += offset
	} else if data[offset] == '{' { // if object value
		dataType = OBJECT
		// break label, for stopping nested loops
		endOffset = trailingBracket(data[offset:], '{', '}')

		if endOffset == -1 {
			return []byte{}, dataType, offset, errors.New("Value looks like object, but can't find closing '}' symbol")
		}

		endOffset += offset
	} else {
		// Number, Boolean or None
		end := bytes.IndexFunc(data[endOffset:], func(c rune) bool {
			return c == ',' || c == '}' || c == ']'
		})

		if data[offset] == 't' || data[offset] == 'f' { // true or false
			dataType = BOOLEAN
		} else if data[offset] == 'u' || data[offset] == 'n' { // undefined or null
			dataType = NULL
		} else {
			dataType = NUMBER
		}

		if end == -1 {
			return []byte{}, dataType, offset, errors.New("Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol")
		}

		endOffset += end
	}

	value = data[offset:endOffset]

	// Strip quotes from string values
	if dataType == STRING {
		value = value[1 : len(value)-1]
	}

	if dataType == NULL {
		value = []byte{}
	}

	return value, dataType, endOffset, nil
}

// Used for iterating arrays, accepts callback function with same return arguments as `Get`.
// Expects to receive array data structure (you need to `Get` it first). See example above.
// Underneeth it just calls `Get` without arguments until it can't find next item.
func ArrayEach(data []byte, cb func(value []byte, dataType int, offset int, err error)) {
	if len(data) == 0 {
		return
	}

	offset := 1
	for true {
		v, t, o, e := Get(data[offset:])

		if t != NOT_EXIST {
			cb(v, t, o, e)
		}

		if e != nil {
			break
		}

		offset += o
	}
}

// Returns same arguments as `Get` except `dataType`.
// If key data type do not match, it will return error.
func GetNumber(data []byte, keys ...string) (val float64, offset int, err error) {
	v, t, offset, e := Get(data, keys...)

	if e != nil {
		return 0, offset, e
	}

	if t != NUMBER {
		return 0, offset, errors.New(fmt.Sprintf("Value is not a number: %s", string(v)))
	}

	val, err = strconv.ParseFloat(string(v), 64)
	return
}

// Returns same arguments as `Get` except `dataType`.
// If key data type do not match, it will return error.
func GetBoolean(data []byte, keys ...string) (val bool, offset int, err error) {
	v, t, offset, e := Get(data, keys...)

	if e != nil {
		return false, offset, e
	}

	if t != BOOLEAN {
		return false, offset, errors.New(fmt.Sprintf("Value is not a boolean: %s", string(v)))
	}

	if v['0'] == 't' {
		val = true
	} else {
		val = false
	}

	return
}
<commit_msg>Remove debug code<commit_after>package jsonparser

import (
	"bytes"
	"errors"
	"fmt"
	d "runtime/debug"
	"strconv"
)

// Find position of next character which is not ' ', ',', '}' or ']'
func nextValue(data []byte) (offset int) {
	for true {
		if len(data) == offset {
			return -1
		}

		if data[offset] != ' ' && data[offset] != '\n' && data[offset] != ',' && data[offset] != '}' && data[offset] != ']' {
			return
		}

		offset += 1
	}

	return -1
}

// Tries to find the end of string
// Support if string contains escaped quote symbols.
func stringEnd(data []byte) int {
	i := 0

	for true {
		sIdx := bytes.IndexByte(data[i:], '"')

		if sIdx == -1 {
			return -1
		}

		i += sIdx + 1

		// If it just escaped \", continue
		if i > 2 && data[i-2] == '\\' {
			continue
		}

		break
	}

	return i
}

// Find end of the data structure, array or object.
// For array openSym and closeSym will be '[' and ']', for object '{' and '}'
// Know about nested structures
func trailingBracket(data []byte, openSym byte, closeSym byte) int {
	level := 0
	i := 0
	ln := len(data)

	for true {
		if i >= ln {
			return -1
		}

		c := data[i]

		// If inside string, skip it
		if c == '"' {
			//sFrom := i
			i++

			se := stringEnd(data[i:])
			if se == -1 {
				return -1
			}
			i += se - 1
		}

		if c == openSym {
			level += 1
		} else if c == closeSym {
			level -= 1
		}

		i++

		if level == 0 {
			break
		}
	}

	return i
}

const (
	NOT_EXIST = iota
	STRING
	NUMBER
	OBJECT
	ARRAY
	BOOLEAN
	NULL
)

/*
Get - Receives data structure, and key path to extract value from.

Returns:
`value` - Pointer to original data structure containing key value, or just empty slice if nothing found or error
`dataType` -    Can be: `NOT_EXIST`, `STRING`, `NUMBER`, `OBJECT`, `ARRAY`, `BOOLEAN` or `NULL`
`offset` - Offset from provided data structure where key value ends. Used mostly internally, for example for `ArrayEach` helper.
`err` - If key not found or any other parsing issue it should return error. If key not found it also sets `dataType` to `NOT_EXISTS`

Accept multiple keys to specify path to JSON value (in case of quering nested structures).
If no keys provided it will try to extract closest JSON value (simple ones or object/array), useful for reading streams or arrays, see `ArrayEach` implementation.
*/
func Get(data []byte, keys ...string) (value []byte, dataType int, offset int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("Unhandler JSON parsing error: %v, %s", r, string(d.Stack())))
		}
	}()

	ln := len(data)

	if len(keys) > 0 {
		for _, k := range keys {
			lk := len(k)

			for true {
				if idx := bytes.Index(data[offset:], []byte(k)); idx != -1 && (ln-(offset+idx+lk+2)) > 0 {
					offset += idx

					if data[offset+lk] == '"' && data[offset-1] == '"' && data[offset+lk+1] == ':' {
						offset += lk + 2
						nO := nextValue(data[offset:])

						if nO == -1 {
							return []byte{}, NOT_EXIST, -1, errors.New("Malformed JSON error")
						}

						offset += nO

						break
					} else {
						offset += 1
					}
				} else {
					return []byte{}, NOT_EXIST, -1, errors.New("Key path not found")
				}
			}
		}
	} else {
		nO := nextValue(data[offset:])

		if nO == -1 {
			return []byte{}, NOT_EXIST, -1, errors.New("Malformed JSON error")
		}

		offset = nO
	}

	endOffset := offset

	// if string value
	if data[offset] == '"' {
		dataType = STRING
		if idx := stringEnd(data[offset+1:]); idx != -1 {
			endOffset += idx + 1
		} else {
			return []byte{}, dataType, offset, errors.New("Value is string, but can't find closing '\"' symbol")
		}
	} else if data[offset] == '[' { // if array value
		dataType = ARRAY
		// break label, for stopping nested loops
		endOffset = trailingBracket(data[offset:], '[', ']')

		if endOffset == -1 {
			return []byte{}, dataType, offset, errors.New("Value is array, but can't find closing ']' symbol")
		}

		endOffset += offset
	} else if data[offset] == '{' { // if object value
		dataType = OBJECT
		// break label, for stopping nested loops
		endOffset = trailingBracket(data[offset:], '{', '}')

		if endOffset == -1 {
			return []byte{}, dataType, offset, errors.New("Value looks like object, but can't find closing '}' symbol")
		}

		endOffset += offset
	} else {
		// Number, Boolean or None
		end := bytes.IndexFunc(data[endOffset:], func(c rune) bool {
			return c == ',' || c == '}' || c == ']'
		})

		if data[offset] == 't' || data[offset] == 'f' { // true or false
			dataType = BOOLEAN
		} else if data[offset] == 'u' || data[offset] == 'n' { // undefined or null
			dataType = NULL
		} else {
			dataType = NUMBER
		}

		if end == -1 {
			return []byte{}, dataType, offset, errors.New("Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol")
		}

		endOffset += end
	}

	value = data[offset:endOffset]

	// Strip quotes from string values
	if dataType == STRING {
		value = value[1 : len(value)-1]
	}

	if dataType == NULL {
		value = []byte{}
	}

	return value, dataType, endOffset, nil
}

// Used for iterating arrays, accepts callback function with same return arguments as `Get`.
// Expects to receive array data structure (you need to `Get` it first). See example above.
// Underneeth it just calls `Get` without arguments until it can't find next item.
func ArrayEach(data []byte, cb func(value []byte, dataType int, offset int, err error)) {
	if len(data) == 0 {
		return
	}

	offset := 1
	for true {
		v, t, o, e := Get(data[offset:])

		if t != NOT_EXIST {
			cb(v, t, o, e)
		}

		if e != nil {
			break
		}

		offset += o
	}
}

// Returns same arguments as `Get` except `dataType`.
// If key data type do not match, it will return error.
func GetNumber(data []byte, keys ...string) (val float64, offset int, err error) {
	v, t, offset, e := Get(data, keys...)

	if e != nil {
		return 0, offset, e
	}

	if t != NUMBER {
		return 0, offset, errors.New(fmt.Sprintf("Value is not a number: %s", string(v)))
	}

	val, err = strconv.ParseFloat(string(v), 64)
	return
}

// Returns same arguments as `Get` except `dataType`.
// If key data type do not match, it will return error.
func GetBoolean(data []byte, keys ...string) (val bool, offset int, err error) {
	v, t, offset, e := Get(data, keys...)

	if e != nil {
		return false, offset, e
	}

	if t != BOOLEAN {
		return false, offset, errors.New(fmt.Sprintf("Value is not a boolean: %s", string(v)))
	}

	if v['0'] == 't' {
		val = true
	} else {
		val = false
	}

	return
}
<|endoftext|>


<commit_before>package kickass

import (
	"strconv"

	"gopkg.in/xmlpath.v2"
)

// XPATH
var (
	xpathTorrentResults = xmlpath.MustCompile("//tr[contains(@id, 'torrent_')]")
	xpathTorrentName    = xmlpath.MustCompile(".//a[@class=\"cellMainLink\"]")
	xpathTorrentURL     = xmlpath.MustCompile(".//a[contains(@title,'Download torrent file')]/@href")
	xpathMagnetURL      = xmlpath.MustCompile(".//a[contains(@title,'Torrent magnet link')]/@href")
	xpathSeed           = xmlpath.MustCompile(".//td[5]")
	xpathLeech          = xmlpath.MustCompile(".//td[6]")
	xpathAge            = xmlpath.MustCompile(".//td[4]")
	xpathSize           = xmlpath.MustCompile(".//td[2]")
	xpathFileCount      = xmlpath.MustCompile(".//td[3]")
	xpathVerify         = xmlpath.MustCompile(".//a[contains(@class,'iverify')]")
	xpathUser           = xmlpath.MustCompile(".//a[contains(@href, '/user/')]")
)

// Default parse function, to be overwritten during the tests
var parseFunc = parseResult

func parseResult(root *xmlpath.Node) ([]*Torrent, error) {
	torrents := []*Torrent{}
	iter := xpathTorrentResults.Iter(root)
	for iter.Next() {
		name, ok := xpathTorrentName.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		torrentURL, ok := xpathTorrentURL.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		magnet, ok := xpathMagnetURL.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		verify := xpathVerify.Exists(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}

		seedStr, ok := xpathSeed.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		seed, err := strconv.Atoi(seedStr)
		if err != nil {
			return nil, err
		}

		leechStr, ok := xpathLeech.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		leech, err := strconv.Atoi(leechStr)
		if err != nil {
			return nil, err
		}

		age, ok := xpathAge.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		fileCountStr, ok := xpathFileCount.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		fileCount, err := strconv.Atoi(fileCountStr)
		if err != nil {
			return nil, err
		}

		size, ok := xpathSize.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}

		user, ok := xpathUser.String(iter.Node())
		if !ok {
			// The user name is not always present
			user = "anonymous"
		}

		t := &Torrent{
			Name:       name,
			TorrentURL: torrentURL,
			MagnetURL:  magnet,
			Seed:       seed,
			Leech:      leech,
			Age:        age,
			FileCount:  fileCount,
			Size:       size,
			Verified:   verify,
			User:       user,
		}

		torrents = append(torrents, t)
	}

	return torrents, nil
}
<commit_msg>Return an empty string if no user found<commit_after>package kickass

import (
	"strconv"

	"gopkg.in/xmlpath.v2"
)

// XPATH
var (
	xpathTorrentResults = xmlpath.MustCompile("//tr[contains(@id, 'torrent_')]")
	xpathTorrentName    = xmlpath.MustCompile(".//a[@class=\"cellMainLink\"]")
	xpathTorrentURL     = xmlpath.MustCompile(".//a[contains(@title,'Download torrent file')]/@href")
	xpathMagnetURL      = xmlpath.MustCompile(".//a[contains(@title,'Torrent magnet link')]/@href")
	xpathSeed           = xmlpath.MustCompile(".//td[5]")
	xpathLeech          = xmlpath.MustCompile(".//td[6]")
	xpathAge            = xmlpath.MustCompile(".//td[4]")
	xpathSize           = xmlpath.MustCompile(".//td[2]")
	xpathFileCount      = xmlpath.MustCompile(".//td[3]")
	xpathVerify         = xmlpath.MustCompile(".//a[contains(@class,'iverify')]")
	xpathUser           = xmlpath.MustCompile(".//a[contains(@href, '/user/')]")
)

// Default parse function, to be overwritten during the tests
var parseFunc = parseResult

func parseResult(root *xmlpath.Node) ([]*Torrent, error) {
	torrents := []*Torrent{}
	iter := xpathTorrentResults.Iter(root)
	for iter.Next() {
		name, ok := xpathTorrentName.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		torrentURL, ok := xpathTorrentURL.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		magnet, ok := xpathMagnetURL.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		verify := xpathVerify.Exists(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}

		seedStr, ok := xpathSeed.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		seed, err := strconv.Atoi(seedStr)
		if err != nil {
			return nil, err
		}

		leechStr, ok := xpathLeech.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		leech, err := strconv.Atoi(leechStr)
		if err != nil {
			return nil, err
		}

		age, ok := xpathAge.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		fileCountStr, ok := xpathFileCount.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}
		fileCount, err := strconv.Atoi(fileCountStr)
		if err != nil {
			return nil, err
		}

		size, ok := xpathSize.String(iter.Node())
		if !ok {
			return nil, ErrUnexpectedContent
		}

		user, ok := xpathUser.String(iter.Node())
		if !ok {
			// The user name is not always present
			user = ""
		}

		t := &Torrent{
			Name:       name,
			TorrentURL: torrentURL,
			MagnetURL:  magnet,
			Seed:       seed,
			Leech:      leech,
			Age:        age,
			FileCount:  fileCount,
			Size:       size,
			Verified:   verify,
			User:       user,
		}

		torrents = append(torrents, t)
	}

	return torrents, nil
}
<|endoftext|>