// Copyright 2017 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 cryptobyte_test

import (
	"errors"
	"fmt"

	"internal/x/crypto/cryptobyte"
	"internal/x/crypto/cryptobyte/asn1"
)

func ExampleString_lengthPrefixed() {
	// This is an example of parsing length-prefixed data (as found in, for
	// example, TLS). Imagine a 16-bit prefixed series of 8-bit prefixed
	// strings.

	input := cryptobyte.String([]byte{0, 12, 5, 'h', 'e', 'l', 'l', 'o', 5, 'w', 'o', 'r', 'l', 'd'})
	var result []string

	var values cryptobyte.String
	if !input.ReadUint16LengthPrefixed(&values) ||
		!input.Empty() {
		panic("bad format")
	}

	for !values.Empty() {
		var value cryptobyte.String
		if !values.ReadUint8LengthPrefixed(&value) {
			panic("bad format")
		}

		result = append(result, string(value))
	}

	// Output: []string{"hello", "world"}
	fmt.Printf("%#v\n", result)
}

func ExampleString_aSN1() {
	// This is an example of parsing ASN.1 data that looks like:
	//    Foo ::= SEQUENCE {
	//      version [6] INTEGER DEFAULT 0
	//      data OCTET STRING
	//    }

	input := cryptobyte.String([]byte{0x30, 12, 0xa6, 3, 2, 1, 2, 4, 5, 'h', 'e', 'l', 'l', 'o'})

	var (
		version                   int64
		data, inner, versionBytes cryptobyte.String
		haveVersion               bool
	)
	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
		!input.Empty() ||
		!inner.ReadOptionalASN1(&versionBytes, &haveVersion, asn1.Tag(6).Constructed().ContextSpecific()) ||
		(haveVersion && !versionBytes.ReadASN1Integer(&version)) ||
		(haveVersion && !versionBytes.Empty()) ||
		!inner.ReadASN1(&data, asn1.OCTET_STRING) ||
		!inner.Empty() {
		panic("bad format")
	}

	// Output: haveVersion: true, version: 2, data: hello
	fmt.Printf("haveVersion: %t, version: %d, data: %s\n", haveVersion, version, string(data))
}

func ExampleBuilder_aSN1() {
	// This is an example of building ASN.1 data that looks like:
	//    Foo ::= SEQUENCE {
	//      version [6] INTEGER DEFAULT 0
	//      data OCTET STRING
	//    }

	version := int64(2)
	data := []byte("hello")
	const defaultVersion = 0

	var b cryptobyte.Builder
	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
		if version != defaultVersion {
			b.AddASN1(asn1.Tag(6).Constructed().ContextSpecific(), func(b *cryptobyte.Builder) {
				b.AddASN1Int64(version)
			})
		}
		b.AddASN1OctetString(data)
	})

	result, err := b.Bytes()
	if err != nil {
		panic(err)
	}

	// Output: 300ca603020102040568656c6c6f
	fmt.Printf("%x\n", result)
}

func ExampleBuilder_lengthPrefixed() {
	// This is an example of building length-prefixed data (as found in,
	// for example, TLS). Imagine a 16-bit prefixed series of 8-bit
	// prefixed strings.
	input := []string{"hello", "world"}

	var b cryptobyte.Builder
	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
		for _, value := range input {
			b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
				b.AddBytes([]byte(value))
			})
		}
	})

	result, err := b.Bytes()
	if err != nil {
		panic(err)
	}

	// Output: 000c0568656c6c6f05776f726c64
	fmt.Printf("%x\n", result)
}

func ExampleBuilder_lengthPrefixOverflow() {
	// Writing more data that can be expressed by the length prefix results
	// in an error from Bytes().

	tooLarge := make([]byte, 256)

	var b cryptobyte.Builder
	b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
		b.AddBytes(tooLarge)
	})

	result, err := b.Bytes()
	fmt.Printf("len=%d err=%s\n", len(result), err)

	// Output: len=0 err=cryptobyte: pending child length 256 exceeds 1-byte length prefix
}

func ExampleBuilderContinuation_errorHandling() {
	var b cryptobyte.Builder
	// Continuations that panic with a BuildError will cause Bytes to
	// return the inner error.
	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
		b.AddUint32(0)
		panic(cryptobyte.BuildError{Err: errors.New("example error")})
	})

	result, err := b.Bytes()
	fmt.Printf("len=%d err=%s\n", len(result), err)

	// Output: len=0 err=example error
}
