// Copyright 2021 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 fuzz

import (
	
	
	
	
	
	
	
	
	
)

// encVersion1 will be the first line of a file with version 1 encoding.
var encVersion1 = "go test fuzz v1"

// marshalCorpusFile encodes an arbitrary number of arguments into the file format for the
// corpus.
func marshalCorpusFile( ...any) []byte {
	if len() == 0 {
		panic("must have at least one value to marshal")
	}
	 := bytes.NewBuffer([]byte(encVersion1 + "\n"))
	// TODO(katiehockman): keep uint8 and int32 encoding where applicable,
	// instead of changing to byte and rune respectively.
	for ,  := range  {
		switch t := .(type) {
		case int, int8, int16, int64, uint, uint16, uint32, uint64, bool:
			fmt.Fprintf(, "%T(%v)\n", , )
		case float32:
			if math.IsNaN(float64()) && math.Float32bits() != math.Float32bits(float32(math.NaN())) {
				// We encode unusual NaNs as hex values, because that is how users are
				// likely to encounter them in literature about floating-point encoding.
				// This allows us to reproduce fuzz failures that depend on the specific
				// NaN representation (for float32 there are about 2^24 possibilities!),
				// not just the fact that the value is *a* NaN.
				//
				// Note that the specific value of float32(math.NaN()) can vary based on
				// whether the architecture represents signaling NaNs using a low bit
				// (as is common) or a high bit (as commonly implemented on MIPS
				// hardware before around 2012). We believe that the increase in clarity
				// from identifying "NaN" with math.NaN() is worth the slight ambiguity
				// from a platform-dependent value.
				fmt.Fprintf(, "math.Float32frombits(0x%x)\n", math.Float32bits())
			} else {
				// We encode all other values — including the NaN value that is
				// bitwise-identical to float32(math.Nan()) — using the default
				// formatting, which is equivalent to strconv.FormatFloat with format
				// 'g' and can be parsed by strconv.ParseFloat.
				//
				// For an ordinary floating-point number this format includes
				// sufficiently many digits to reconstruct the exact value. For positive
				// or negative infinity it is the string "+Inf" or "-Inf". For positive
				// or negative zero it is "0" or "-0". For NaN, it is the string "NaN".
				fmt.Fprintf(, "%T(%v)\n", , )
			}
		case float64:
			if math.IsNaN() && math.Float64bits() != math.Float64bits(math.NaN()) {
				fmt.Fprintf(, "math.Float64frombits(0x%x)\n", math.Float64bits())
			} else {
				fmt.Fprintf(, "%T(%v)\n", , )
			}
		case string:
			fmt.Fprintf(, "string(%q)\n", )
		case rune: // int32
			// Although rune and int32 are represented by the same type, only a subset
			// of valid int32 values can be expressed as rune literals. Notably,
			// negative numbers, surrogate halves, and values above unicode.MaxRune
			// have no quoted representation.
			//
			// fmt with "%q" (and the corresponding functions in the strconv package)
			// would quote out-of-range values to the Unicode replacement character
			// instead of the original value (see https://go.dev/issue/51526), so
			// they must be treated as int32 instead.
			//
			// We arbitrarily draw the line at UTF-8 validity, which biases toward the
			// "rune" interpretation. (However, we accept either format as input.)
			if utf8.ValidRune() {
				fmt.Fprintf(, "rune(%q)\n", )
			} else {
				fmt.Fprintf(, "int32(%v)\n", )
			}
		case byte: // uint8
			// For bytes, we arbitrarily prefer the character interpretation.
			// (Every byte has a valid character encoding.)
			fmt.Fprintf(, "byte(%q)\n", )
		case []byte: // []uint8
			fmt.Fprintf(, "[]byte(%q)\n", )
		default:
			panic(fmt.Sprintf("unsupported type: %T", ))
		}
	}
	return .Bytes()
}

// unmarshalCorpusFile decodes corpus bytes into their respective values.
func unmarshalCorpusFile( []byte) ([]any, error) {
	if len() == 0 {
		return nil, fmt.Errorf("cannot unmarshal empty string")
	}
	 := bytes.Split(, []byte("\n"))
	if len() < 2 {
		return nil, fmt.Errorf("must include version and at least one value")
	}
	 := strings.TrimSuffix(string([0]), "\r")
	if  != encVersion1 {
		return nil, fmt.Errorf("unknown encoding version: %s", )
	}
	var  []any
	for ,  := range [1:] {
		 = bytes.TrimSpace()
		if len() == 0 {
			continue
		}
		,  := parseCorpusValue()
		if  != nil {
			return nil, fmt.Errorf("malformed line %q: %v", , )
		}
		 = append(, )
	}
	return , nil
}

func parseCorpusValue( []byte) (any, error) {
	 := token.NewFileSet()
	,  := parser.ParseExprFrom(, "(test)", , 0)
	if  != nil {
		return nil, 
	}
	,  := .(*ast.CallExpr)
	if ! {
		return nil, fmt.Errorf("expected call expression")
	}
	if len(.Args) != 1 {
		return nil, fmt.Errorf("expected call expression with 1 argument; got %d", len(.Args))
	}
	 := .Args[0]

	if ,  := .Fun.(*ast.ArrayType);  {
		if .Len != nil {
			return nil, fmt.Errorf("expected []byte or primitive type")
		}
		,  := .Elt.(*ast.Ident)
		if ! || .Name != "byte" {
			return nil, fmt.Errorf("expected []byte")
		}
		,  := .(*ast.BasicLit)
		if ! || .Kind != token.STRING {
			return nil, fmt.Errorf("string literal required for type []byte")
		}
		,  := strconv.Unquote(.Value)
		if  != nil {
			return nil, 
		}
		return []byte(), nil
	}

	var  *ast.Ident
	if ,  := .Fun.(*ast.SelectorExpr);  {
		,  := .X.(*ast.Ident)
		if ! || .Name != "math" {
			return nil, fmt.Errorf("invalid selector type")
		}
		switch .Sel.Name {
		case "Float64frombits":
			 = &ast.Ident{Name: "float64-bits"}
		case "Float32frombits":
			 = &ast.Ident{Name: "float32-bits"}
		default:
			return nil, fmt.Errorf("invalid selector type")
		}
	} else {
		,  = .Fun.(*ast.Ident)
		if ! {
			return nil, fmt.Errorf("expected []byte or primitive type")
		}
		if .Name == "bool" {
			,  := .(*ast.Ident)
			if ! {
				return nil, fmt.Errorf("malformed bool")
			}
			if .Name == "true" {
				return true, nil
			} else if .Name == "false" {
				return false, nil
			} else {
				return nil, fmt.Errorf("true or false required for type bool")
			}
		}
	}

	var (
		  string
		 token.Token
	)
	if ,  := .(*ast.UnaryExpr);  {
		switch lit := .X.(type) {
		case *ast.BasicLit:
			if .Op != token.SUB {
				return nil, fmt.Errorf("unsupported operation on int/float: %v", .Op)
			}
			// Special case for negative numbers.
			 = .Op.String() + .Value // e.g. "-" + "124"
			 = .Kind
		case *ast.Ident:
			if .Name != "Inf" {
				return nil, fmt.Errorf("expected operation on int or float type")
			}
			if .Op == token.SUB {
				 = "-Inf"
			} else {
				 = "+Inf"
			}
			 = token.FLOAT
		default:
			return nil, fmt.Errorf("expected operation on int or float type")
		}
	} else {
		switch lit := .(type) {
		case *ast.BasicLit:
			,  = .Value, .Kind
		case *ast.Ident:
			if .Name != "NaN" {
				return nil, fmt.Errorf("literal value required for primitive type")
			}
			,  = "NaN", token.FLOAT
		default:
			return nil, fmt.Errorf("literal value required for primitive type")
		}
	}

	switch  := .Name;  {
	case "string":
		if  != token.STRING {
			return nil, fmt.Errorf("string literal value required for type string")
		}
		return strconv.Unquote()
	case "byte", "rune":
		if  == token.INT {
			switch  {
			case "rune":
				return parseInt(, )
			case "byte":
				return parseUint(, )
			}
		}
		if  != token.CHAR {
			return nil, fmt.Errorf("character literal required for byte/rune types")
		}
		 := len()
		if  < 2 {
			return nil, fmt.Errorf("malformed character literal, missing single quotes")
		}
		, , ,  := strconv.UnquoteChar([1:-1], '\'')
		if  != nil {
			return nil, 
		}
		if  == "rune" {
			return , nil
		}
		if  >= 256 {
			return nil, fmt.Errorf("can only encode single byte to a byte type")
		}
		return byte(), nil
	case "int", "int8", "int16", "int32", "int64":
		if  != token.INT {
			return nil, fmt.Errorf("integer literal required for int types")
		}
		return parseInt(, )
	case "uint", "uint8", "uint16", "uint32", "uint64":
		if  != token.INT {
			return nil, fmt.Errorf("integer literal required for uint types")
		}
		return parseUint(, )
	case "float32":
		if  != token.FLOAT &&  != token.INT {
			return nil, fmt.Errorf("float or integer literal required for float32 type")
		}
		,  := strconv.ParseFloat(, 32)
		return float32(), 
	case "float64":
		if  != token.FLOAT &&  != token.INT {
			return nil, fmt.Errorf("float or integer literal required for float64 type")
		}
		return strconv.ParseFloat(, 64)
	case "float32-bits":
		if  != token.INT {
			return nil, fmt.Errorf("integer literal required for math.Float32frombits type")
		}
		,  := parseUint(, "uint32")
		if  != nil {
			return nil, 
		}
		return math.Float32frombits(.(uint32)), nil
	case "float64-bits":
		if  != token.FLOAT &&  != token.INT {
			return nil, fmt.Errorf("integer literal required for math.Float64frombits type")
		}
		,  := parseUint(, "uint64")
		if  != nil {
			return nil, 
		}
		return math.Float64frombits(.(uint64)), nil
	default:
		return nil, fmt.Errorf("expected []byte or primitive type")
	}
}

// parseInt returns an integer of value val and type typ.
func parseInt(,  string) (any, error) {
	switch  {
	case "int":
		// The int type may be either 32 or 64 bits. If 32, the fuzz tests in the
		// corpus may include 64-bit values produced by fuzzing runs on 64-bit
		// architectures. When running those tests, we implicitly wrap the values to
		// fit in a regular int. (The test case is still “interesting”, even if the
		// specific values of its inputs are platform-dependent.)
		,  := strconv.ParseInt(, 0, 64)
		return int(), 
	case "int8":
		,  := strconv.ParseInt(, 0, 8)
		return int8(), 
	case "int16":
		,  := strconv.ParseInt(, 0, 16)
		return int16(), 
	case "int32", "rune":
		,  := strconv.ParseInt(, 0, 32)
		return int32(), 
	case "int64":
		return strconv.ParseInt(, 0, 64)
	default:
		panic("unreachable")
	}
}

// parseUint returns an unsigned integer of value val and type typ.
func parseUint(,  string) (any, error) {
	switch  {
	case "uint":
		,  := strconv.ParseUint(, 0, 64)
		return uint(), 
	case "uint8", "byte":
		,  := strconv.ParseUint(, 0, 8)
		return uint8(), 
	case "uint16":
		,  := strconv.ParseUint(, 0, 16)
		return uint16(), 
	case "uint32":
		,  := strconv.ParseUint(, 0, 32)
		return uint32(), 
	case "uint64":
		return strconv.ParseUint(, 0, 64)
	default:
		panic("unreachable")
	}
}