// Copyright 2014 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.

// This file is a simple protocol buffer encoder and decoder.
//
// A protocol message must implement the message interface:
//   decoder() []decoder
//   encode(*buffer)
//
// The decode method returns a slice indexed by field number that gives the
// function to decode that field.
// The encode method encodes its receiver into the given buffer.
//
// The two methods are simple enough to be implemented by hand rather than
// by using a protocol compiler.
//
// See profile.go for examples of messages implementing this interface.
//
// There is no support for groups, message sets, or "has" bits.

package profile

import (
	
	
)

type buffer struct {
	field int
	typ   int
	u64   uint64
	data  []byte
	tmp   [16]byte
}

type decoder func(*buffer, message) error

type message interface {
	decoder() []decoder
	encode(*buffer)
}

func marshal( message) []byte {
	var  buffer
	.encode(&)
	return .data
}

func encodeVarint( *buffer,  uint64) {
	for  >= 128 {
		.data = append(.data, byte()|0x80)
		 >>= 7
	}
	.data = append(.data, byte())
}

func encodeLength( *buffer,  int,  int) {
	encodeVarint(, uint64()<<3|2)
	encodeVarint(, uint64())
}

func encodeUint64( *buffer,  int,  uint64) {
	// append varint to b.data
	encodeVarint(, uint64()<<3|0)
	encodeVarint(, )
}

func encodeUint64s( *buffer,  int,  []uint64) {
	if len() > 2 {
		// Use packed encoding
		 := len(.data)
		for ,  := range  {
			encodeVarint(, )
		}
		 := len(.data)
		encodeLength(, , -)
		 := len(.data)
		copy(.tmp[:], .data[:])
		copy(.data[+(-):], .data[:])
		copy(.data[:], .tmp[:-])
		return
	}
	for ,  := range  {
		encodeUint64(, , )
	}
}

func encodeUint64Opt( *buffer,  int,  uint64) {
	if  == 0 {
		return
	}
	encodeUint64(, , )
}

func encodeInt64( *buffer,  int,  int64) {
	 := uint64()
	encodeUint64(, , )
}

func encodeInt64Opt( *buffer,  int,  int64) {
	if  == 0 {
		return
	}
	encodeInt64(, , )
}

func encodeInt64s( *buffer,  int,  []int64) {
	if len() > 2 {
		// Use packed encoding
		 := len(.data)
		for ,  := range  {
			encodeVarint(, uint64())
		}
		 := len(.data)
		encodeLength(, , -)
		 := len(.data)
		copy(.tmp[:], .data[:])
		copy(.data[+(-):], .data[:])
		copy(.data[:], .tmp[:-])
		return
	}
	for ,  := range  {
		encodeInt64(, , )
	}
}

func encodeString( *buffer,  int,  string) {
	encodeLength(, , len())
	.data = append(.data, ...)
}

func encodeStrings( *buffer,  int,  []string) {
	for ,  := range  {
		encodeString(, , )
	}
}

func encodeStringOpt( *buffer,  int,  string) {
	if  == "" {
		return
	}
	encodeString(, , )
}

func encodeBool( *buffer,  int,  bool) {
	if  {
		encodeUint64(, , 1)
	} else {
		encodeUint64(, , 0)
	}
}

func encodeBoolOpt( *buffer,  int,  bool) {
	if  == false {
		return
	}
	encodeBool(, , )
}

func encodeMessage( *buffer,  int,  message) {
	 := len(.data)
	.encode()
	 := len(.data)
	encodeLength(, , -)
	 := len(.data)
	copy(.tmp[:], .data[:])
	copy(.data[+(-):], .data[:])
	copy(.data[:], .tmp[:-])
}

func unmarshal( []byte,  message) ( error) {
	 := buffer{data: , typ: 2}
	return decodeMessage(&, )
}

func le64( []byte) uint64 {
	return uint64([0]) | uint64([1])<<8 | uint64([2])<<16 | uint64([3])<<24 | uint64([4])<<32 | uint64([5])<<40 | uint64([6])<<48 | uint64([7])<<56
}

func le32( []byte) uint32 {
	return uint32([0]) | uint32([1])<<8 | uint32([2])<<16 | uint32([3])<<24
}

func decodeVarint( []byte) (uint64, []byte, error) {
	var  int
	var  uint64
	for  = 0; ; ++ {
		if  >= 10 ||  >= len() {
			return 0, nil, errors.New("bad varint")
		}
		 |= uint64([]&0x7F) << uint(7*)
		if []&0x80 == 0 {
			return , [+1:], nil
		}
	}
}

func decodeField( *buffer,  []byte) ([]byte, error) {
	, ,  := decodeVarint()
	if  != nil {
		return nil, 
	}
	.field = int( >> 3)
	.typ = int( & 7)
	.data = nil
	.u64 = 0
	switch .typ {
	case 0:
		.u64, ,  = decodeVarint()
		if  != nil {
			return nil, 
		}
	case 1:
		if len() < 8 {
			return nil, errors.New("not enough data")
		}
		.u64 = le64([:8])
		 = [8:]
	case 2:
		var  uint64
		, ,  = decodeVarint()
		if  != nil {
			return nil, 
		}
		if  > uint64(len()) {
			return nil, errors.New("too much data")
		}
		.data = [:]
		 = [:]
	case 5:
		if len() < 4 {
			return nil, errors.New("not enough data")
		}
		.u64 = uint64(le32([:4]))
		 = [4:]
	default:
		return nil, fmt.Errorf("unknown wire type: %d", .typ)
	}

	return , nil
}

func checkType( *buffer,  int) error {
	if .typ !=  {
		return errors.New("type mismatch")
	}
	return nil
}

func decodeMessage( *buffer,  message) error {
	if  := checkType(, 2);  != nil {
		return 
	}
	 := .decoder()
	 := .data
	for len() > 0 {
		// pull varint field# + type
		var  error
		,  = decodeField(, )
		if  != nil {
			return 
		}
		if .field >= len() || [.field] == nil {
			continue
		}
		if  := [.field](, );  != nil {
			return 
		}
	}
	return nil
}

func decodeInt64( *buffer,  *int64) error {
	if  := checkType(, 0);  != nil {
		return 
	}
	* = int64(.u64)
	return nil
}

func decodeInt64s( *buffer,  *[]int64) error {
	if .typ == 2 {
		// Packed encoding
		 := .data
		for len() > 0 {
			var  uint64
			var  error

			if , ,  = decodeVarint();  != nil {
				return 
			}
			* = append(*, int64())
		}
		return nil
	}
	var  int64
	if  := decodeInt64(, &);  != nil {
		return 
	}
	* = append(*, )
	return nil
}

func decodeUint64( *buffer,  *uint64) error {
	if  := checkType(, 0);  != nil {
		return 
	}
	* = .u64
	return nil
}

func decodeUint64s( *buffer,  *[]uint64) error {
	if .typ == 2 {
		 := .data
		// Packed encoding
		for len() > 0 {
			var  uint64
			var  error

			if , ,  = decodeVarint();  != nil {
				return 
			}
			* = append(*, )
		}
		return nil
	}
	var  uint64
	if  := decodeUint64(, &);  != nil {
		return 
	}
	* = append(*, )
	return nil
}

func decodeString( *buffer,  *string) error {
	if  := checkType(, 2);  != nil {
		return 
	}
	* = string(.data)
	return nil
}

func decodeStrings( *buffer,  *[]string) error {
	var  string
	if  := decodeString(, &);  != nil {
		return 
	}
	* = append(*, )
	return nil
}

func decodeBool( *buffer,  *bool) error {
	if  := checkType(, 0);  != nil {
		return 
	}
	if int64(.u64) == 0 {
		* = false
	} else {
		* = true
	}
	return nil
}