File goipp-1.2.0.obscpio of Package goipp

07070100000000000081A400000000000000000000000167F56AFB00000013000000000000000000000000000000000000001700000000goipp-1.2.0/.gitignoreipp-usb
tags
*.swp
07070100000001000081A400000000000000000000000167F56AFB0000052F000000000000000000000000000000000000001400000000goipp-1.2.0/LICENSEBSD 2-Clause License

Copyright (c) 2020, Alexander Pevzner
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
07070100000002000081A400000000000000000000000167F56AFB00000034000000000000000000000000000000000000001500000000goipp-1.2.0/Makefileall:
	-gotags -R . > tags
	go build

test:
	go test
07070100000003000081A400000000000000000000000167F56AFB0000035A000000000000000000000000000000000000001600000000goipp-1.2.0/README.md# goipp

[![godoc.org](https://godoc.org/github.com/OpenPrinting/goipp?status.svg)](http://godoc.org/github.com/OpenPrinting/goipp)
![GitHub](https://img.shields.io/github/license/OpenPrinting/goipp)
[![Go Report Card](https://goreportcard.com/badge/github.com/OpenPrinting/goipp)](https://goreportcard.com/report/github.com/OpenPrinting/goipp)

The goipp library is fairly complete implementation of IPP core protocol in
pure Go. Essentially, it is  IPP messages parser/composer. Transport is
not implemented here, because Go standard library has an excellent built-in
HTTP client, and it doesn't make a lot of sense to wrap it here.

High-level requests, like "print a file" are also not implemented, only the
low-level stuff.

All documentation is on godoc.org -- follow the link above. Pull requests
are welcomed, assuming they don't break existing API.
07070100000004000081A400000000000000000000000167F56AFB0000001D000000000000000000000000000000000000001800000000goipp-1.2.0/_config.ymltheme: jekyll-theme-architect07070100000005000081A400000000000000000000000167F56AFB00001285000000000000000000000000000000000000001400000000goipp-1.2.0/attr.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Message attributes
 */

package goipp

import (
	"fmt"
	"sort"
)

// Attributes represents a slice of attributes
type Attributes []Attribute

// Add Attribute to Attributes
func (attrs *Attributes) Add(attr Attribute) {
	*attrs = append(*attrs, attr)
}

// Clone creates a shallow copy of Attributes.
// For nil input it returns nil output.
func (attrs Attributes) Clone() Attributes {
	var attrs2 Attributes
	if attrs != nil {
		attrs2 = make(Attributes, len(attrs))
		copy(attrs2, attrs)
	}
	return attrs2
}

// DeepCopy creates a deep copy of Attributes.
// For nil input it returns nil output.
func (attrs Attributes) DeepCopy() Attributes {
	var attrs2 Attributes
	if attrs != nil {
		attrs2 = make(Attributes, len(attrs))
		for i := range attrs {
			attrs2[i] = attrs[i].DeepCopy()
		}
	}
	return attrs2
}

// Equal checks that attrs and attrs2 are equal
//
// Note, Attributes(nil) and Attributes{} are not Equal but Similar.
func (attrs Attributes) Equal(attrs2 Attributes) bool {
	if len(attrs) != len(attrs2) {
		return false
	}

	if (attrs == nil) != (attrs2 == nil) {
		return false
	}

	for i, attr := range attrs {
		attr2 := attrs2[i]
		if !attr.Equal(attr2) {
			return false
		}
	}

	return true
}

// Similar checks that attrs and attrs2 are **logically** equal,
// which means the following:
//   - attrs and addrs2 contain the same set of attributes,
//     but may be differently ordered
//   - Values of attributes of the same name within attrs and
//     attrs2 are similar
//
// Note, Attributes(nil) and Attributes{} are not Equal but Similar.
func (attrs Attributes) Similar(attrs2 Attributes) bool {
	// Fast check: if lengths are not the same, attributes
	// are definitely not equal
	if len(attrs) != len(attrs2) {
		return false
	}

	// Sort attrs and attrs2 by name
	sorted1 := attrs.Clone()
	sort.SliceStable(sorted1, func(i, j int) bool {
		return sorted1[i].Name < sorted1[j].Name
	})

	sorted2 := attrs2.Clone()
	sort.SliceStable(sorted2, func(i, j int) bool {
		return sorted2[i].Name < sorted2[j].Name
	})

	// And now compare sorted slices
	for i, attr1 := range sorted1 {
		attr2 := sorted2[i]
		if !attr1.Similar(attr2) {
			return false
		}
	}

	return true
}

// Attribute represents a single attribute, which consist of
// the Name and one or more Values
type Attribute struct {
	Name   string // Attribute name
	Values Values // Slice of values
}

// MakeAttribute makes Attribute with single value.
//
// Deprecated. Use [MakeAttr] instead.
func MakeAttribute(name string, tag Tag, value Value) Attribute {
	attr := Attribute{Name: name}
	attr.Values.Add(tag, value)
	return attr
}

// MakeAttr makes Attribute with one or more values.
func MakeAttr(name string, tag Tag, val1 Value, values ...Value) Attribute {
	attr := Attribute{Name: name}
	attr.Values.Add(tag, val1)
	for _, val := range values {
		attr.Values.Add(tag, val)
	}
	return attr
}

// MakeAttrCollection makes [Attribute] with [Collection] value.
func MakeAttrCollection(name string,
	member1 Attribute, members ...Attribute) Attribute {

	col := make(Collection, len(members)+1)
	col[0] = member1
	copy(col[1:], members)

	return MakeAttribute(name, TagBeginCollection, col)
}

// Equal checks that Attribute is equal to another Attribute
// (i.e., names are the same and values are equal)
func (a Attribute) Equal(a2 Attribute) bool {
	return a.Name == a2.Name && a.Values.Equal(a2.Values)
}

// Similar checks that Attribute is **logically** equal to another
// Attribute (i.e., names are the same and values are similar)
func (a Attribute) Similar(a2 Attribute) bool {
	return a.Name == a2.Name && a.Values.Similar(a2.Values)
}

// DeepCopy creates a deep copy of the Attribute
func (a Attribute) DeepCopy() Attribute {
	a2 := a
	a2.Values = a2.Values.DeepCopy()
	return a2
}

// Unpack attribute value from its wire representation
func (a *Attribute) unpack(tag Tag, value []byte) error {
	var err error
	var val Value

	switch tag.Type() {
	case TypeVoid, TypeCollection:
		val = Void{}

	case TypeInteger:
		val = Integer(0)

	case TypeBoolean:
		val = Boolean(false)

	case TypeString:
		val = String("")

	case TypeDateTime:
		val = Time{}

	case TypeResolution:
		val = Resolution{}

	case TypeRange:
		val = Range{}

	case TypeTextWithLang:
		val = TextWithLang{}

	case TypeBinary:
		val = Binary(nil)

	default:
		panic(fmt.Sprintf("(Attribute) uppack(): tag=%s type=%s", tag, tag.Type()))
	}

	val, err = val.decode(value)

	if err == nil {
		a.Values.Add(tag, val)
	} else {
		err = fmt.Errorf("%s: %s", tag, err)
	}

	return err
}
07070100000006000081A400000000000000000000000167F56AFB00001D10000000000000000000000000000000000000001900000000goipp-1.2.0/attr_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Message attributes tests
 */

package goipp

import (
	"errors"
	"testing"
	"time"
	"unsafe"
)

// TestAttributesEqualSimilar tests Attributes.Equal and
// Attributes.Similar methods
func TestAttributesEqualSimilar(t *testing.T) {
	type testData struct {
		a1, a2  Attributes // A pair of Attributes slice
		equal   bool       // Expected a1.Equal(a2) output
		similar bool       // Expected a2.Similar(a2) output

	}

	tests := []testData{
		{
			// nil Attributes are equal and similar
			a1:      nil,
			a2:      nil,
			equal:   true,
			similar: true,
		},

		{
			// Empty Attributes are equal and similar
			a1:      Attributes{},
			a2:      Attributes{},
			equal:   true,
			similar: true,
		},

		{
			// Attributes(nil) vs Attributes{} are similar but not equal
			a1:      Attributes{},
			a2:      nil,
			equal:   false,
			similar: true,
		},

		{
			// Attributes of different length are neither equal nor similar
			a1: Attributes{
				MakeAttr("attr1", TagInteger, Integer(0)),
			},
			a2:      Attributes{},
			equal:   false,
			similar: false,
		},
		{
			// Same Attributes are equal and similar
			a1: Attributes{
				MakeAttr("attr1", TagInteger, Integer(0)),
			},
			a2: Attributes{
				MakeAttr("attr1", TagInteger, Integer(0)),
			},
			equal:   true,
			similar: true,
		},
		{
			// Same tag, different value: not equal or similar
			a1: Attributes{
				MakeAttr("attr1", TagInteger, Integer(0)),
			},
			a2: Attributes{
				MakeAttr("attr1", TagInteger, Integer(1)),
			},
			equal:   false,
			similar: false,
		},
		{
			// Same value, tag value: not equal or similar
			a1: Attributes{
				MakeAttr("attr1", TagInteger, Integer(0)),
			},
			a2: Attributes{
				MakeAttr("attr1", TagEnum, Integer(0)),
			},
			equal:   false,
			similar: false,
		},

		{
			// Different but similar Value types:
			// Attributes are not equal but similar
			a1: Attributes{
				MakeAttr("attr1", TagString, Binary("hello")),
				MakeAttr("attr2", TagString, String("world")),
			},
			a2: Attributes{
				MakeAttr("attr1", TagString, String("hello")),
				MakeAttr("attr2", TagString, Binary("world")),
			},
			equal:   false,
			similar: true,
		},

		{
			// Different order: not equal but similar
			a1: Attributes{
				MakeAttr("attr1", TagString, String("hello")),
				MakeAttr("attr2", TagString, String("world")),
			},
			a2: Attributes{
				MakeAttr("attr2", TagString, String("world")),
				MakeAttr("attr1", TagString, String("hello")),
			},
			equal:   false,
			similar: true,
		},
	}

	for _, test := range tests {
		equal := test.a1.Equal(test.a2)
		similar := test.a1.Similar(test.a2)

		if equal != test.equal {
			t.Errorf("testing Attributes.Equal:\n"+
				"attrs 1:   %s\n"+
				"attrs 2:   %s\n"+
				"expected:  %v\n"+
				"present:   %v\n",
				test.a1, test.a2,
				test.equal, equal,
			)
		}

		if similar != test.similar {
			t.Errorf("testing Attributes.Similar:\n"+
				"attrs 1:  %s\n"+
				"attrs 2:  %s\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.a1, test.a2,
				test.similar, similar,
			)
		}
	}
}

// TestAttributesConstructors tests Attributes.Add and MakeAttr
func TestAttributesConstructors(t *testing.T) {
	attrs1 := Attributes{
		Attribute{
			Name: "attr1",
			Values: Values{
				{TagString, String("hello")},
				{TagString, String("world")},
			},
		},
		Attribute{
			Name: "attr2",
			Values: Values{
				{TagInteger, Integer(1)},
				{TagInteger, Integer(2)},
				{TagInteger, Integer(3)},
			},
		},
	}

	attrs2 := Attributes{}
	attrs2.Add(MakeAttr("attr1", TagString, String("hello"), String("world")))
	attrs2.Add(MakeAttr("attr2", TagInteger, Integer(1), Integer(2), Integer(3)))

	if !attrs1.Equal(attrs2) {
		t.Errorf("Attributes constructors test failed")
	}
}

// TestMakeAttribute tests MakeAttribute function
func TestMakeAttribute(t *testing.T) {
	a1 := Attribute{
		Name:   "attr",
		Values: Values{{TagInteger, Integer(1)}},
	}

	a2 := MakeAttribute("attr", TagInteger, Integer(1))

	if !a1.Equal(a2) {
		t.Errorf("MakeAttribute test failed")
	}
}

// TestAttributesCopy tests Attributes.Clone and Attributes.DeepCopy
func TestAttributesCopy(t *testing.T) {
	type testData struct {
		attrs Attributes
	}

	tests := []testData{
		{nil},
		{Attributes{}},
		{
			Attributes{
				MakeAttr("attr1", TagString, String("hello"), String("world")),
				MakeAttr("attr2", TagInteger, Integer(1), Integer(2), Integer(3)),
				MakeAttr("attr2", TagBoolean, Boolean(true), Boolean(false)),
			},
		},
	}

	for _, test := range tests {
		clone := test.attrs.Clone()

		if !test.attrs.Equal(clone) {
			t.Errorf("testing Attributes.Clone\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.attrs,
				clone,
			)
		}

		copy := test.attrs.DeepCopy()
		if !test.attrs.Equal(copy) {
			t.Errorf("testing Attributes.DeepCopy\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.attrs,
				copy,
			)
		}
	}
}

// TestAttributeUnpack tests Attribute.unpack method for all kinds
// of Value types
func TestAttributeUnpack(t *testing.T) {
	loc := time.FixedZone("UTC+3:30", 3*3600+1800)
	tm, _ := time.ParseInLocation(
		time.RFC3339, "2025-03-29T16:48:53+03:30", loc)

	values := Values{
		{TagBoolean, Boolean(true)},
		{TagExtension, Binary{}},
		{TagString, Binary{1, 2, 3}},
		{TagInteger, Integer(123)},
		{TagEnum, Integer(-321)},
		{TagRange, Range{-100, 200}},
		{TagRange, Range{-100, -50}},
		{TagResolution, Resolution{150, 300, UnitsDpi}},
		{TagResolution, Resolution{100, 200, UnitsDpcm}},
		{TagResolution, Resolution{75, 150, 10}},
		{TagName, String("hello")},
		{TagTextLang, TextWithLang{"en-US", "hello"}},
		{TagDateTime, Time{tm}},
		{TagNoValue, Void{}},
	}

	for _, v := range values {
		expected := Attribute{Name: "attr", Values: Values{v}}
		present := Attribute{Name: "attr"}
		data, _ := v.V.encode()
		present.unpack(v.T, data)

		if !expected.Equal(present) {
			t.Errorf("%x %d", data, unsafe.Sizeof(int(5)))
			t.Errorf("testing Attribute.unpack:\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				expected, present,
			)
		}
	}
}

// TestAttributeUnpackError tests that Attribute.unpack properly
// handles errors from the Value.decode
func TestAttributeUnpackError(t *testing.T) {
	noError := errors.New("")

	type testData struct {
		t    Tag    // Input value tag
		data []byte // Input data
		err  string // Expected error
	}

	tests := []testData{
		{
			t:    TagInteger,
			data: []byte{},
			err:  "integer: value must be 4 bytes",
		},

		{
			t:    TagBoolean,
			data: []byte{},
			err:  "boolean: value must be 1 byte",
		},
	}

	for _, test := range tests {
		attr := Attribute{Name: "attr"}
		err := attr.unpack(test.t, test.data)
		if err == nil {
			err = noError
		}

		if err.Error() != test.err {
			t.Errorf("testing Attribute.unpack:\n"+
				"input tag:      %s\n"+
				"input data:     %x\n"+
				"error expected: %s\n"+
				"error present:  %s\n",
				test.t, test.data,
				test.err, err,
			)

		}
	}
}

// TestAttributeUnpackPanic tests that Attribute.unpack panics
// on invalid Tag
func TestAttributeUnpackPanic(t *testing.T) {
	defer func() {
		recover()
	}()

	attr := Attribute{Name: "attr"}
	attr.unpack(TagOperationGroup, []byte{})

	t.Errorf("Attribute.unpack must panic on the invalid Tag")
}
07070100000007000081A400000000000000000000000167F56AFB0000019D000000000000000000000000000000000000001500000000goipp-1.2.0/const.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Various constants
 */

package goipp

const (
	// ContentType is the HTTP content type for IPP messages
	ContentType = "application/ipp"

	// msgPrintIndent used for indentation by message pretty-printer
	msgPrintIndent = "    "
)
07070100000008000081A400000000000000000000000167F56AFB000025E4000000000000000000000000000000000000001700000000goipp-1.2.0/decoder.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Message decoder
 */

package goipp

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
)

// DecoderOptions represents message decoder options
type DecoderOptions struct {
	// EnableWorkarounds, if set to true, enables various workarounds
	// for decoding IPP messages that violate IPP protocol specification
	//
	// Currently it includes the following workarounds:
	// * Pantum M7300FDW violates collection encoding rules.
	//   Instead of using TagMemberName, it uses named attributes
	//   within the collection
	//
	// The list of implemented workarounds may grow in the
	// future
	EnableWorkarounds bool
}

// messageDecoder represents Message decoder
type messageDecoder struct {
	in  io.Reader      // Input stream
	off int            // Offset of last read
	cnt int            // Count of read bytes
	opt DecoderOptions // Options
}

// Decode the message
func (md *messageDecoder) decode(m *Message) error {
	// Wire format:
	//
	//   2 bytes:  Version
	//   2 bytes:  Code (Operation or Status)
	//   4 bytes:  RequestID
	//   variable: attributes
	//   1 byte:   TagEnd

	// Parse message header
	var err error
	m.Version, err = md.decodeVersion()
	if err == nil {
		m.Code, err = md.decodeCode()
	}
	if err == nil {
		m.RequestID, err = md.decodeU32()
	}

	// Now parse attributes
	done := false
	var group *Attributes
	var attr Attribute
	var prev *Attribute

	for err == nil && !done {
		var tag Tag
		tag, err = md.decodeTag()

		if err != nil {
			break
		}

		if tag.IsDelimiter() {
			prev = nil
		}

		if tag.IsGroup() {
			m.Groups.Add(Group{tag, nil})
		}

		switch tag {
		case TagZero:
			err = errors.New("Invalid tag 0")
		case TagEnd:
			done = true

		case TagOperationGroup:
			group = &m.Operation
		case TagJobGroup:
			group = &m.Job
		case TagPrinterGroup:
			group = &m.Printer
		case TagUnsupportedGroup:
			group = &m.Unsupported
		case TagSubscriptionGroup:
			group = &m.Subscription
		case TagEventNotificationGroup:
			group = &m.EventNotification
		case TagResourceGroup:
			group = &m.Resource
		case TagDocumentGroup:
			group = &m.Document
		case TagSystemGroup:
			group = &m.System
		case TagFuture11Group:
			group = &m.Future11
		case TagFuture12Group:
			group = &m.Future12
		case TagFuture13Group:
			group = &m.Future13
		case TagFuture14Group:
			group = &m.Future14
		case TagFuture15Group:
			group = &m.Future15

		default:
			// Decode attribute
			if tag == TagMemberName || tag == TagEndCollection {
				err = fmt.Errorf("Unexpected tag %s", tag)
			} else {
				attr, err = md.decodeAttribute(tag)
			}

			if err == nil && tag == TagBeginCollection {
				attr.Values[0].V, err = md.decodeCollection()
			}

			// If everything is OK, save attribute
			switch {
			case err != nil:
			case attr.Name == "":
				if prev != nil {
					prev.Values.Add(attr.Values[0].T, attr.Values[0].V)

					// Append value to the last Attribute of the
					// last Group in the m.Groups
					//
					// Note, if we are here, this last Attribute definitely exists,
					// because:
					//   * prev != nil
					//   * prev is set when new named attribute is added
					//   * prev is reset when delimiter tag is encountered
					gLast := &m.Groups[len(m.Groups)-1]
					aLast := &gLast.Attrs[len(gLast.Attrs)-1]
					aLast.Values.Add(attr.Values[0].T, attr.Values[0].V)
				} else {
					err = errors.New("Additional value without preceding attribute")
				}
			case group != nil:
				group.Add(attr)
				prev = &(*group)[len(*group)-1]
				m.Groups[len(m.Groups)-1].Add(attr)
			default:
				err = errors.New("Attribute without a group")
			}
		}
	}

	if err != nil {
		err = fmt.Errorf("%s at 0x%x", err, md.off)
	}

	return err
}

// Decode a Collection
//
// Collection is like a nested object - an attribute which value is a sequence
// of named attributes. Collections can be nested.
//
// Wire format:
//
//	ATTR: Tag = TagBeginCollection,            - the outer attribute that
//	      Name = "name", value - ignored         contains the collection
//
//	ATTR: Tag = TagMemberName, name = "",      - member name \
//	      value - string, name of the next                    |
//	      member                                              | repeated for
//	                                                          | each member
//	ATTR: Tag = any attribute tag, name = "",  - repeated for |
//	      value = member value                   multi-value /
//	                                             members
//
//	ATTR: Tag = TagEndCollection, name = "",
//	      value - ignored
//
// The format looks a bit baroque, but please note that it was added
// in the IPP 2.0. For IPP 1.x collection looks like a single multi-value
// TagBeginCollection attribute (attributes without names considered
// next value for the previously defined named attributes) and so
// 1.x parser silently ignores collections and doesn't get confused
// with them.
func (md *messageDecoder) decodeCollection() (Collection, error) {
	collection := make(Collection, 0)

	memberName := ""

	for {
		tag, err := md.decodeTag()
		if err != nil {
			return nil, err
		}

		// Delimiter cannot be inside a collection
		if tag.IsDelimiter() {
			err = fmt.Errorf("Collection: unexpected tag %s", tag)
			return nil, err
		}

		// Check for TagMemberName without the subsequent value attribute
		if (tag == TagMemberName || tag == TagEndCollection) && memberName != "" {
			err = fmt.Errorf("Collection: unexpected %s, expected value tag", tag)
			return nil, err
		}

		// Fetch next attribute
		attr, err := md.decodeAttribute(tag)
		if err != nil {
			return nil, err
		}

		// Process next attribute
		switch tag {
		case TagEndCollection:
			return collection, nil

		case TagMemberName:
			memberName = string(attr.Values[0].V.(String))
			if memberName == "" {
				err = fmt.Errorf("Collection: %s value is empty", tag)
				return nil, err
			}

		case TagBeginCollection:
			// Decode nested collection
			attr.Values[0].V, err = md.decodeCollection()
			if err != nil {
				return nil, err
			}
			fallthrough

		default:
			if md.opt.EnableWorkarounds &&
				memberName == "" && attr.Name != "" {
				// Workaround for: Pantum M7300FDW
				//
				// This device violates collection encoding rules.
				// Instead of using TagMemberName, it uses named
				// attributes within the collection
				memberName = attr.Name
			}

			if memberName != "" {
				attr.Name = memberName
				collection = append(collection, attr)
				memberName = ""
			} else if len(collection) > 0 {
				l := len(collection)
				collection[l-1].Values.Add(tag, attr.Values[0].V)
			} else {
				// We've got a value without preceding TagMemberName
				err = fmt.Errorf("Collection: unexpected %s, expected %s", tag, TagMemberName)
				return nil, err
			}
		}
	}
}

// Decode a tag
func (md *messageDecoder) decodeTag() (Tag, error) {
	t, err := md.decodeU8()

	return Tag(t), err
}

// Decode a Version
func (md *messageDecoder) decodeVersion() (Version, error) {
	code, err := md.decodeU16()
	return Version(code), err
}

// Decode a Code
func (md *messageDecoder) decodeCode() (Code, error) {
	code, err := md.decodeU16()
	return Code(code), err
}

// Decode a single attribute
//
// Wire format:
//
//	1   byte:   Tag
//	2+N bytes:  Name length (2 bytes) + name string
//	2+N bytes:  Value length (2 bytes) + value bytes
//
// For the extended tag format, Tag is encoded as TagExtension and
// 4 bytes of the actual tag value prepended to the value bytes
func (md *messageDecoder) decodeAttribute(tag Tag) (Attribute, error) {
	var attr Attribute
	var value []byte
	var err error

	// Obtain attribute name and raw value
	attr.Name, err = md.decodeString()
	if err != nil {
		goto ERROR
	}

	value, err = md.decodeBytes()
	if err != nil {
		goto ERROR
	}

	// Handle TagExtension
	if tag == TagExtension {
		if len(value) < 4 {
			err = errors.New("Extension tag truncated")
			goto ERROR
		}

		t := binary.BigEndian.Uint32(value[:4])

		if t > 0x7fffffff {
			err = fmt.Errorf(
				"Extension tag 0x%8.8x out of range", t)
			goto ERROR
		}
	}

	// Unpack value
	err = attr.unpack(tag, value)
	if err != nil {
		goto ERROR
	}

	return attr, nil

	// Return a error
ERROR:
	return Attribute{}, err
}

// Decode a 8-bit integer
func (md *messageDecoder) decodeU8() (uint8, error) {
	buf := make([]byte, 1)
	err := md.read(buf)
	return buf[0], err
}

// Decode a 16-bit integer
func (md *messageDecoder) decodeU16() (uint16, error) {
	buf := make([]byte, 2)
	err := md.read(buf)
	return binary.BigEndian.Uint16(buf[:]), err
}

// Decode a 32-bit integer
func (md *messageDecoder) decodeU32() (uint32, error) {
	buf := make([]byte, 4)
	err := md.read(buf)
	return binary.BigEndian.Uint32(buf[:]), err
}

// Decode sequence of bytes
func (md *messageDecoder) decodeBytes() ([]byte, error) {
	length, err := md.decodeU16()
	if err != nil {
		return nil, err
	}

	data := make([]byte, length)
	err = md.read(data)
	if err != nil {
		return nil, err
	}

	return data, nil
}

// Decode string
func (md *messageDecoder) decodeString() (string, error) {
	data, err := md.decodeBytes()
	if err != nil {
		return "", err
	}

	return string(data), nil
}

// Read a piece of raw data from input stream
func (md *messageDecoder) read(data []byte) error {
	md.off = md.cnt

	for len(data) > 0 {
		n, err := md.in.Read(data)
		if n > 0 {
			md.cnt += n
			data = data[n:]
		} else {
			md.off = md.cnt
			if err == nil || err == io.EOF {
				err = errors.New("Message truncated")
			}
			return err
		}

	}

	return nil
}
07070100000009000081A400000000000000000000000167F56AFB000010A3000000000000000000000000000000000000001300000000goipp-1.2.0/doc.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Package documentation
 */

/*
Package goipp implements IPP core protocol, as defined by RFC 8010

It doesn't implement high-level operations, such as "print a document",
"cancel print job" and so on. It's scope is limited to proper generation
and parsing of IPP requests and responses.

	IPP protocol uses the following simple model:
	1. Send a request
	2. Receive a response

Request and response both has a similar format, represented here
by type Message, with the only difference, that Code field of
that Message is the Operation code in request and Status code
in response. So most of operations are common for request and
response messages

# Example (Get-Printer-Attributes):

	package main

	import (
		"bytes"
		"net/http"
		"os"

		"github.com/OpenPrinting/goipp"
	)

	const uri = "http://192.168.1.102:631"

	// Build IPP OpGetPrinterAttributes request
	func makeRequest() ([]byte, error) {
		m := goipp.NewRequest(goipp.DefaultVersion, goipp.OpGetPrinterAttributes, 1)
		m.Operation.Add(goipp.MakeAttribute("attributes-charset",
			goipp.TagCharset, goipp.String("utf-8")))
		m.Operation.Add(goipp.MakeAttribute("attributes-natural-language",
			goipp.TagLanguage, goipp.String("en-US")))
		m.Operation.Add(goipp.MakeAttribute("printer-uri",
			goipp.TagURI, goipp.String(uri)))
		m.Operation.Add(goipp.MakeAttribute("requested-attributes",
			goipp.TagKeyword, goipp.String("all")))

		return m.EncodeBytes()
	}

	// Check that there is no error
	func check(err error) {
		if err != nil {
			panic(err)
		}
	}

	func main() {
		request, err := makeRequest()
		check(err)

		resp, err := http.Post(uri, goipp.ContentType, bytes.NewBuffer(request))
		check(err)

		var respMsg goipp.Message

		err = respMsg.Decode(resp.Body)
		check(err)

		respMsg.Print(os.Stdout, false)
	}

# Example (Print PDF file):

	package main

	import (
		"bytes"
		"errors"
		"fmt"
		"io"
		"net/http"
		"os"

		"github.com/OpenPrinting/goipp"
	)

	const (
		PrinterURL = "ipp://192.168.1.102:631/ipp/print"
		TestPage   = "onepage-a4.pdf"
	)

	// checkErr checks for an error. If err != nil, it prints error
	// message and exits
	func checkErr(err error, format string, args ...interface{}) {
		if err != nil {
			msg := fmt.Sprintf(format, args...)
			fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err)
			os.Exit(1)
		}
	}

	// ExamplePrintPDF demo
	func main() {
		// Build and encode IPP request
		req := goipp.NewRequest(goipp.DefaultVersion, goipp.OpPrintJob, 1)
		req.Operation.Add(goipp.MakeAttribute("attributes-charset",
			goipp.TagCharset, goipp.String("utf-8")))
		req.Operation.Add(goipp.MakeAttribute("attributes-natural-language",
			goipp.TagLanguage, goipp.String("en-US")))
		req.Operation.Add(goipp.MakeAttribute("printer-uri",
			goipp.TagURI, goipp.String(PrinterURL)))
		req.Operation.Add(goipp.MakeAttribute("requesting-user-name",
			goipp.TagName, goipp.String("John Doe")))
		req.Operation.Add(goipp.MakeAttribute("job-name",
			goipp.TagName, goipp.String("job name")))
		req.Operation.Add(goipp.MakeAttribute("document-format",
			goipp.TagMimeType, goipp.String("application/pdf")))

		payload, err := req.EncodeBytes()
		checkErr(err, "IPP encode")

		// Open document file
		file, err := os.Open(TestPage)
		checkErr(err, "Open document file")

		defer file.Close()

		// Build HTTP request
		body := io.MultiReader(bytes.NewBuffer(payload), file)

		httpReq, err := http.NewRequest(http.MethodPost, PrinterURL, body)
		checkErr(err, "HTTP")

		httpReq.Header.Set("content-type", goipp.ContentType)
		httpReq.Header.Set("accept", goipp.ContentType)

		// Execute HTTP request
		httpRsp, err := http.DefaultClient.Do(httpReq)
		if httpRsp != nil {
			defer httpRsp.Body.Close()
		}

		checkErr(err, "HTTP")

		if httpRsp.StatusCode/100 != 2 {
			checkErr(errors.New(httpRsp.Status), "HTTP")
		}

		// Decode IPP response
		rsp := &goipp.Message{}
		err = rsp.Decode(httpRsp.Body)
		checkErr(err, "IPP decode")

		if goipp.Status(rsp.Code) != goipp.StatusOk {
			err = errors.New(goipp.Status(rsp.Code).String())
			checkErr(err, "IPP")
		}
	}
*/
package goipp
0707010000000A000081A400000000000000000000000167F56AFB00020AEF000000000000000000000000000000000000001B00000000goipp-1.2.0/encdec_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Encoder/decoder test
 */

package goipp

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"testing"
	"time"
)

// errWriter implements io.Writer interface.
//
// It accepts some first bytes and after that always returns an error.
// It is used to test I/O error return from the Message.Encode function.
type errWriter struct{ skip int }

var _ = io.Writer(&errWriter{})

func (ewr *errWriter) Write(data []byte) (int, error) {
	if len(data) <= ewr.skip {
		ewr.skip -= len(data)
		return len(data), nil
	}

	n := ewr.skip
	ewr.skip = 0

	return n, errors.New("I/O error")
}

// errValue implements Value interface and returns
// error from its encode() and decode() methods
type errValue struct{}

var _ = Value(errValue{})

func (errValue) String() string  { return "" }
func (errValue) Type() Type      { return TypeInteger }
func (errValue) DeepCopy() Value { return errValue{} }

func (errValue) encode() ([]byte, error) {
	return nil, errors.New("encode error")
}

func (errValue) decode([]byte) (Value, error) {
	return nil, errors.New("decode error")
}

// Check that err == nil
func assertNoError(t *testing.T, err error) {
	if err != nil {
		t.Errorf("%s", err)
	}
}

// Check that err != nil
func assertWithError(t *testing.T, err error) {
	if err == nil {
		t.Errorf("Error expected")
	}
}

// Check that err != nil and contains expected test
func assertErrorIs(t *testing.T, err error, s string) {
	if err == nil {
		if s != "" {
			t.Errorf("Error expected")
		}
		return
	}

	if !strings.HasPrefix(err.Error(), s) {
		t.Errorf("Error is %q, expected %q", err, s)
	}
}

// parseTime parses time given in time.Layout format.
// this function panics if time cannot be parsed
func parseTime(s string) Time {
	const layout = "01/02 03:04:05PM '06 -0700" // Missed in go 1.11
	t, err := time.Parse(layout, s)
	if err != nil {
		panic(fmt.Sprintf("parseTime(%q): %s", s, err))
	}
	return Time{t}
}

// testEncodeDecodeMessage creates a quite complex message
// for Encode/Decode test
func testEncodeDecodeMessage() *Message {
	m := &Message{
		Version:   DefaultVersion,
		Code:      0x1234,
		RequestID: 0x87654321,
	}

	// Populate all groups
	m.Operation.Add(MakeAttribute("grp_operation", TagInteger,
		Integer(1)))
	m.Job.Add(MakeAttribute("grp_job", TagInteger,
		Integer(2)))
	m.Printer.Add(MakeAttribute("grp_printer", TagInteger,
		Integer(3)))
	m.Unsupported.Add(MakeAttribute("grp_unsupported", TagInteger,
		Integer(4)))
	m.Subscription.Add(MakeAttribute("grp_subscription", TagInteger,
		Integer(5)))
	m.EventNotification.Add(MakeAttribute("grp_eventnotification", TagInteger,
		Integer(6)))
	m.Resource.Add(MakeAttribute("grp_resource", TagInteger,
		Integer(7)))
	m.Document.Add(MakeAttribute("grp_document", TagInteger,
		Integer(8)))
	m.System.Add(MakeAttribute("grp_system", TagInteger,
		Integer(9)))
	m.Future11.Add(MakeAttribute("grp_future11", TagInteger,
		Integer(10)))
	m.Future12.Add(MakeAttribute("grp_future12", TagInteger,
		Integer(11)))
	m.Future13.Add(MakeAttribute("grp_future13", TagInteger,
		Integer(12)))
	m.Future14.Add(MakeAttribute("grp_future14", TagInteger,
		Integer(13)))
	m.Future15.Add(MakeAttribute("grp_future15", TagInteger,
		Integer(14)))

	// Use all possible attribute types
	m.Operation.Add(MakeAttribute("type_integer", TagInteger, Integer(123)))

	m.Operation.Add(MakeAttribute("type_boolean_t", TagBoolean, Boolean(true)))
	m.Operation.Add(MakeAttribute("type_boolean_f", TagBoolean, Boolean(false)))

	m.Operation.Add(MakeAttribute("type_void", TagUnsupportedValue, Void{}))

	m.Operation.Add(MakeAttribute("type_string_1", TagText, String("hello")))
	m.Operation.Add(MakeAttribute("type_string_2", TagText, String("")))

	m.Operation.Add(MakeAttribute("type_time_1", TagDateTime,
		parseTime("01/02 03:04:05PM '06 -0700")))
	m.Operation.Add(MakeAttribute("type_time_2", TagDateTime,
		parseTime("01/02 03:04:05PM '06 +0700")))
	m.Operation.Add(MakeAttribute("type_time_3", TagDateTime,
		parseTime("01/02 03:04:05PM '06 -0730")))

	m.Operation.Add(MakeAttribute("type_resolution_1", TagResolution,
		Resolution{123, 456, UnitsDpi}))
	m.Operation.Add(MakeAttribute("type_resolution_2", TagResolution,
		Resolution{78, 90, UnitsDpcm}))

	m.Operation.Add(MakeAttribute("type_range", TagRange,
		Range{100, 1000}))

	m.Operation.Add(MakeAttribute("type_textlang_1", TagTextLang,
		TextWithLang{"hello", "en"}))
	m.Operation.Add(MakeAttribute("type_textlang_1", TagTextLang,
		TextWithLang{"привет", "ru"}))

	return m
}

// Test Message Encode, then Decode
func TestEncodeDecode(t *testing.T) {
	m1 := testEncodeDecodeMessage()

	data, err := m1.EncodeBytes()
	assertNoError(t, err)

	m2 := &Message{}
	err = m2.DecodeBytes(data)
	assertNoError(t, err)
}

// Test encode errors
func TestEncodeErrors(t *testing.T) {
	// Attribute without name
	m := NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a := MakeAttribute("attr", TagInteger, Integer(123))
	a.Name = ""
	m.Operation.Add(a)
	err := m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Attribute without name")

	// Attribute without value
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagInteger, Integer(123))
	a.Values = nil
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Attribute without value")

	// Attribute name exceeds...
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagInteger, Integer(123))
	a.Name = strings.Repeat("x", 32767)
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertNoError(t, err)

	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagInteger, Integer(123))
	a.Name = strings.Repeat("x", 32767+1)
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Attribute name exceeds 32767 bytes")

	// Attribute value exceeds...
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagText, String(strings.Repeat("x", 32767)))
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertNoError(t, err)

	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagText, String(strings.Repeat("x", 32767+1)))
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Attribute value exceeds 32767 bytes")

	// Tag XXX cannot be used with value
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagJobGroup, Integer(123))
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Tag job-attributes-tag cannot be used with value")

	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagMemberName, Integer(123))
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Tag memberAttrName cannot be used with value")

	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagEndCollection, Integer(123))
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Tag endCollection cannot be used with value")

	// Collection member without name
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagBeginCollection, Collection{
		MakeAttribute("", TagInteger, Integer(123)),
	})
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Collection member without name")

	// Tag XXX: YYY value required, ZZZ present
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagText, Integer(123))
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "Tag textWithoutLanguage: String value required, Integer present")

	// I/O error
	m = testEncodeDecodeMessage()

	data, err := m.EncodeBytes()
	assertNoError(t, err)

	for skip := 0; skip < len(data); skip++ {
		err = m.Encode(&errWriter{skip})
		assertErrorIs(t, err, "I/O error")
	}

	// encode error
	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagInteger, errValue{})
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "encode error")

	m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
	a = MakeAttribute("attr", TagBeginCollection, Collection{
		MakeAttribute("attr", TagInteger, errValue{}),
	})
	m.Operation.Add(a)
	err = m.Encode(ioutil.Discard)
	assertErrorIs(t, err, "encode error")
}

// Test decode errors
func TestDecodeErrors(t *testing.T) {
	var d []byte
	var err error
	var m = &Message{}

	hdr := []byte{
		0x01, 0x01, // IPP version
		0x00, 0x02, // Print-Job operation
		0x01, 0x02, 0x03, 0x04, // Request ID
	}

	body := []byte{}

	// Message truncated
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagInteger),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x04, // Value length + value
		0x00, 0x00, 0x54, 0x56,
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Message truncated at")

	d, err = testEncodeDecodeMessage().EncodeBytes()
	assertNoError(t, err)

	for i := 0; i < len(d); i++ {
		err = m.DecodeBytes(d[:i])
		assertErrorIs(t, err, "Message truncated at")
	}

	d = goodMessage1
	for i := 0; i < len(d); i++ {
		err = m.DecodeBytes(d[:i])
		assertErrorIs(t, err, "Message truncated at")
	}

	// Invalid tag 0
	d = append(hdr, 0)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Invalid tag 0 at 0x8")

	// Attribute without a group
	body = []byte{
		uint8(TagInteger),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x04, // Value length + value
		0x00, 0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, uint8(TagJobGroup))
	d = append(d, body...)
	err = m.DecodeBytes(d)
	assertNoError(t, err)

	err = m.DecodeBytes(append(hdr, body...))
	assertErrorIs(t, err, "Attribute without a group at")

	// Additional value without preceding attribute
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagInteger),
		0x00, 0x00, // No name
		0x00, 0x04, // Value length + value
		0x00, 0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	err = m.DecodeBytes(append(hdr, body...))
	assertErrorIs(t, err, "Additional value without preceding attribute")

	// "Unexpected tag XXX"
	for _, tag := range []Tag{TagMemberName, TagEndCollection} {
		body = []byte{
			uint8(TagJobGroup),
			uint8(TagInteger),
			0x00, 0x04, // Name length + name
			'a', 't', 't', 'r',
			0x00, 0x04, // Value length + value
			0x00, 0x00, 0x54, 0x56,
		}

		d = append(hdr, body...)
		d = append(d, uint8(tag))
		d = append(d, uint8(TagEnd))
		err = m.DecodeBytes(d)
		assertErrorIs(t, err, "Unexpected tag")
	}

	// Collection: unexpected tag XXX
	for tag := TagZero; tag.IsDelimiter(); tag++ {
		body = []byte{
			uint8(TagJobGroup),

			uint8(TagBeginCollection),
			0x00, 0x0a, // Name length + name
			'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
			0x00, 0x00, // No value

			uint8(TagMemberName),
			0x00, 0x00, // No name
			0x00, 0x06, // Value length + value
			'm', 'e', 'm', 'b', 'e', 'r',

			uint8(TagInteger),
			0x00, 0x00, // No name
			0x00, 0x04, // Value length + value
			0x00, 0x00, 0x54, 0x56,

			uint8(tag),

			uint8(TagEndCollection),
			0x00, 0x00, // No name
			0x00, 0x00, // No value
		}

		d = append(hdr, body...)
		d = append(d, uint8(TagEnd))
		err = m.DecodeBytes(d)
		assertErrorIs(t, err, "Collection: unexpected tag")
	}

	// Collection: unexpected endCollection, expected value tag
	body = []byte{
		uint8(TagJobGroup),

		uint8(TagBeginCollection),
		0x00, 0x0a, // Name length + name
		'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
		0x00, 0x00, // No value

		uint8(TagMemberName),
		0x00, 0x00, // No name
		0x00, 0x06, // Value length + value
		'm', 'e', 'm', 'b', 'e', 'r',

		uint8(TagEndCollection),
		0x00, 0x00, // No name
		0x00, 0x00, // No value

		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Collection: unexpected endCollection, expected value tag")

	// Collection: unexpected memberAttrName, expected value tag
	body = []byte{
		uint8(TagJobGroup),

		uint8(TagBeginCollection),
		0x00, 0x0a, // Name length + name
		'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
		0x00, 0x00, // No value

		uint8(TagMemberName),
		0x00, 0x00, // No name
		0x00, 0x07, // Value length + value
		'm', 'e', 'm', 'b', 'e', 'r', '1',

		uint8(TagMemberName),
		0x00, 0x00, // No name
		0x00, 0x07, // Value length + value
		'm', 'e', 'm', 'b', 'e', 'r', '2',

		uint8(TagInteger),
		0x00, 0x00, // No name
		0x00, 0x04, // Value length + value
		0x00, 0x00, 0x54, 0x56,

		uint8(TagEndCollection),
		0x00, 0x00, // No name
		0x00, 0x00, // No value

		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Collection: unexpected memberAttrName, expected value tag")

	// Collection: memberAttrName value is empty
	body = []byte{
		uint8(TagJobGroup),

		uint8(TagBeginCollection),
		0x00, 0x0a, // Name length + name
		'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
		0x00, 0x00, // No value

		uint8(TagMemberName),
		0x00, 0x00, // No name
		0x00, 0x00, // No value

		uint8(TagInteger),
		0x00, 0x00, // No name
		0x00, 0x04, // Value length + value
		0x00, 0x00, 0x54, 0x56,

		uint8(TagEndCollection),
		0x00, 0x00, // No name
		0x00, 0x00, // No value

		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Collection: memberAttrName value is empty")

	// Collection: unexpected integer, expected memberAttrName
	body = []byte{
		uint8(TagJobGroup),

		uint8(TagBeginCollection),
		0x00, 0x0a, // Name length + name
		'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
		0x00, 0x00, // No value

		uint8(TagInteger),
		0x00, 0x00, // No name
		0x00, 0x04, // Value length + value
		0x00, 0x00, 0x54, 0x56,

		uint8(TagEndCollection),
		0x00, 0x00, // No name
		0x00, 0x00, // No value

		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Collection: unexpected integer, expected memberAttrName")
}

// Test errors in decoding values
func TestDecodeValueErrors(t *testing.T) {
	var d []byte
	var err error
	var m = &Message{}

	hdr := []byte{
		0x01, 0x01, // IPP version
		0x00, 0x02, // Print-Job operation
		0x01, 0x02, 0x03, 0x04, // Request ID
	}

	body := []byte{}

	// integer: value must be 4 bytes
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagInteger),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length + value
		0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "integer: value must be 4 bytes")

	// boolean: value must be 1 byte
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagBoolean),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length + value
		0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "boolean: value must be 1 byte")

	// dateTime: value must be 11 bytes
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagDateTime),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length + value
		0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "dateTime: value must be 11 bytes")

	// dateTime: bad XXX
	var datetest = []struct {
		in  []byte
		err string
	}{
		//      year        month day   hour  min   sec   s/10
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
			""},
		{[]byte{0x07, 0xe7, 0xff, 0x15, 0x11, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
			"dateTime: bad month 255"},
		{[]byte{0x07, 0xe7, 0x02, 0xff, 0x11, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
			"dateTime: bad day 255"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0xff, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
			"dateTime: bad hours 255"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0xff, 0x32, 0x05, '+', 0x04, 0x00},
			"dateTime: bad minutes 255"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0xff, 0x05, '+', 0x04, 0x00},
			"dateTime: bad seconds 255"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0xff, '+', 0x04, 0x00},
			"dateTime: bad deciseconds 255"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '?', 0x04, 0x00},
			"dateTime: bad UTC sign"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '-', 0xff, 0x00},
			"dateTime: bad UTC hours 255"},
		{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '-', 0x04, 0xff},
			"dateTime: bad UTC minutes 255"},
	}

	for _, test := range datetest {
		body = []byte{
			uint8(TagJobGroup),
			uint8(TagDateTime),
			0x00, 0x04, // Name length + name
			'a', 't', 't', 'r',
			0x00, 0x0b, // Value length + value
		}

		d = append(hdr, body...)
		d = append(d, test.in...)
		d = append(d, uint8(TagEnd))

		err = m.DecodeBytes(d)
		assertErrorIs(t, err, test.err)
	}

	// resolution: value must be 9 bytes
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagResolution),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length + value
		0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "resolution: value must be 9 bytes ")

	// rangeOfInteger: value must be 8 bytes
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagRange),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length + value
		0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "rangeOfInteger: value must be 8 bytes")

	// textWithLanguage: truncated language length
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagTextLang),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x01, // Value length
		0x00,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "textWithLanguage: truncated language length")

	// textWithLanguage: truncated language name
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagTextLang),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length
		0x00, 0x02, // Language length
		'e',
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "textWithLanguage: truncated language name")

	// textWithLanguage: truncated text length
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagTextLang),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x05, // Value length
		0x00, 0x02, // Language length
		'e', 'n', // Language name
		0x00, // Text length
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "textWithLanguage: truncated text length")

	// textWithLanguage: truncated text string
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagTextLang),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x09, // Value length
		0x00, 0x02, // Language length
		'e', 'n', // Language name
		0x00, 0x05, // Text length
		'h', 'e', 'l',
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "textWithLanguage: truncated text string")

	// textWithLanguage: extra 2 bytes at the end of value
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagTextLang),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x0d, // Value length
		0x00, 0x02, // Language length
		'e', 'n', // Language name
		0x00, 0x05, // Text length
		'h', 'e', 'l', 'l', 'o', // Test string
		'?', '?', // Extra 2 bytes
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "textWithLanguage: extra 2 bytes at the end of value")
}

// Test TagExtension
func TestTagExtension(t *testing.T) {
	// Normal tag cannot exceed 0xff and there is no automatic
	// conversion of the high-value tags into extended.
	m1 := NewResponse(DefaultVersion, StatusOk, 0x12345678)
	m1.Operation.Add(MakeAttribute("attr", 0x100, Binary{1, 2, 3, 4, 5}))

	_, err := m1.EncodeBytes()
	assertErrorIs(t, err, "Tag 0x00000100 out of range")

	// Ensure extension tag encodes and decodes well
	m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
	m1.Operation.Add(MakeAttribute("attr", TagExtension,
		Binary{1, 2, 3, 4, 5}))

	data, err := m1.EncodeBytes()
	assertNoError(t, err)

	m2 := Message{}
	err = m2.DecodeBytes(data)
	assertNoError(t, err)

	if !m1.Equal(m2) {
		t.Errorf("Message is not the same after encoding and decoding")
	}

	// Extension tag requires Binary payload
	m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
	m1.Operation.Add(MakeAttribute("attr", TagExtension,
		String("hello")))

	_, err = m1.EncodeBytes()
	assertErrorIs(t, err, "Tag extension: Binary value required, String present")

	// Extension tag requires at least 4 bytes of payload
	m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
	m1.Operation.Add(MakeAttribute("attr", TagExtension,
		Binary{1, 2, 3}))

	_, err = m1.EncodeBytes()
	assertErrorIs(t, err, "Extension tag truncated")

	// Extension tag can't exceed 0x7fffffff, check that encoder
	// validates it.
	m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
	m1.Operation.Add(MakeAttribute("attr", TagExtension,
		Binary{0x80, 1, 2, 3, 4, 5}))

	_, err = m1.EncodeBytes()
	assertErrorIs(t, err, "Extension tag 0x80010203 out of range")

	// Now prepare to decoder tests
	var d []byte
	var m = &Message{}

	hdr := []byte{
		0x01, 0x01, // IPP version
		0x00, 0x02, // Print-Job operation
		0x01, 0x02, 0x03, 0x04, // Request ID
	}

	body := []byte{}

	// Extension tag truncated
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagExtension),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x03, // Value length + value
		0x00, 0x54, 0x56,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Extension tag truncated")

	// Extension tag out of range
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagExtension),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x08, // Value length + value
		0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertErrorIs(t, err, "Extension tag 0xffffffff out of range")

	// Extension tag with the small value is OK
	body = []byte{
		uint8(TagJobGroup),
		uint8(TagExtension),
		0x00, 0x04, // Name length + name
		'a', 't', 't', 'r',
		0x00, 0x08, // Value length + value
		0x00, 0x00, 0x00, 0x01, 0, 0, 0, 0,
		uint8(TagEnd),
	}

	d = append(hdr, body...)
	err = m.DecodeBytes(d)
	assertNoError(t, err)
}

// Test message decoding
func testDecode(t *testing.T, data []byte, opt DecoderOptions,
	mustFail, mustEncode bool) {

	var m Message
	err := m.DecodeBytesEx(data, opt)

	if mustFail {
		assertWithError(t, err)
	} else {
		assertNoError(t, err)
	}

	if err != nil {
		return
	}

	//m.Print(os.Stdout, true)

	if !m.Equal(m) {
		t.Errorf("Message is not equal to itself")
	}

	if mustEncode {
		buf, err := m.EncodeBytes()
		assertNoError(t, err)

		if !bytes.Equal(buf, data) {
			t.Errorf("Message is not the same after decoding and encoding")
		}
	}

	// We can't test a lot of (*Message) Print(), so lets test
	// at least that it doesn't hand
	m.Print(ioutil.Discard, true)
}

func TestDecodeGoodMessage1(t *testing.T) {
	testDecode(t, goodMessage1, DecoderOptions{}, false, true)
}

func TestDecodeGoodMessage2(t *testing.T) {
	testDecode(t, goodMessage2, DecoderOptions{}, false, true)
}

func TestDecodeBadMessage1(t *testing.T) {
	testDecode(t, badMessage1, DecoderOptions{}, true, false)
	testDecode(t, badMessage1,
		DecoderOptions{EnableWorkarounds: true}, false, false)
}

func TestDecodeHPOfficeJetPro8730(t *testing.T) {
	testDecode(t, attrsHPOfficeJetPro8730, DecoderOptions{}, false, true)
}

func TestDecodePantumM7300FDW(t *testing.T) {
	testDecode(t, attrsPantumM7300FDW,
		DecoderOptions{EnableWorkarounds: false}, true, false)

	testDecode(t, attrsPantumM7300FDW,
		DecoderOptions{EnableWorkarounds: true}, false, false)
}

// ------------------------ Test Data ------------------------
// The good message - 1
var goodMessage1 = []byte{
	0x01, 0x01, // IPP version
	0x00, 0x02, // Print-Job operation
	0x00, 0x00, 0x00, 0x01, // Request ID

	uint8(TagOperationGroup),

	uint8(TagCharset),
	0x00, 0x12, // Name length + name
	'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
	'c', 'h', 'a', 'r', 's', 'e', 't',
	0x00, 0x05, // Value length + value
	'u', 't', 'f', '-', '8',

	uint8(TagLanguage),
	0x00, 0x1b, // Name length + name
	'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
	'n', 'a', 't', 'u', 'r', 'a', 'l', '-', 'l', 'a', 'n',
	'g', 'u', 'a', 'g', 'e',
	0x00, 0x02, // Value length + value
	'e', 'n',

	uint8(TagURI),
	0x00, 0x0b, // Name length + name
	'p', 'r', 'i', 'n', 't', 'e', 'r', '-', 'u', 'r', 'i',
	0x00, 0x1c, // Value length + value
	'i', 'p', 'p', ':', '/', '/', 'l', 'o', 'c', 'a', 'l',
	'h', 'o', 's', 't', '/', 'p', 'r', 'i', 'n', 't', 'e',
	'r', 's', '/', 'f', 'o', 'o',

	uint8(TagJobGroup),

	uint8(TagBeginCollection),
	0x00, 0x09, // Name length + name
	'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
	0x00, 0x00, // No value

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0a, // Value length + value
	'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',

	uint8(TagBeginCollection),
	0x00, 0x00, // Name length + name
	0x00, 0x00, // No value

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0b, // Value length + value
	'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',

	uint8(TagInteger),
	0x00, 0x00, // No name
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x54, 0x56,

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0b, // Value length + value
	'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',

	uint8(TagInteger),
	0x00, 0x00, // No name
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x6d, 0x24,

	uint8(TagEndCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0b, // Value length + value
	'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',

	uint8(TagKeyword),
	0x00, 0x00, // No name
	0x00, 0x04, // Value length + value
	'b', 'l', 'u', 'e',

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0a, // Value length + value
	'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',

	uint8(TagKeyword),
	0x00, 0x00, // No name
	0x00, 0x05, // Value length + value
	'p', 'l', 'a', 'i', 'n',

	uint8(TagEndCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagBeginCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0a, // Value length + value
	'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',

	uint8(TagBeginCollection),
	0x00, 0x00, // Name length + name
	0x00, 0x00, // No value

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0b, // Value length + value
	'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',

	uint8(TagInteger),
	0x00, 0x00, // No name
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x52, 0x08,

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0b, // Value length + value
	'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',

	uint8(TagInteger),
	0x00, 0x00, // No name
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x74, 0x04,

	uint8(TagEndCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0b, // Value length + value
	'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',

	uint8(TagKeyword),
	0x00, 0x00, // No name
	0x00, 0x05, // Value length + value
	'p', 'l', 'a', 'i', 'd',

	uint8(TagMemberName),
	0x00, 0x00, // No name
	0x00, 0x0a, // Value length + value
	'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',

	uint8(TagKeyword),
	0x00, 0x00, // No name
	0x00, 0x06, // Value length + value
	'g', 'l', 'o', 's', 's', 'y',

	uint8(TagEndCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagEnd),
}

// The good message - 2
var goodMessage2 = []byte{
	0x01, 0x01, // IPP version
	0x00, 0x02, // Print-Job operation
	0x00, 0x00, 0x00, 0x01, // Request ID

	uint8(TagOperationGroup),

	uint8(TagInteger),
	0x00, 0x1f, // Name length + name
	'n', 'o', 't', 'i', 'f', 'y', '-', 'l', 'e', 'a', 's', 'e',
	'-', 'd', 'u', 'r', 'a', 't', 'i', 'o', 'n', '-', 's', 'u',
	'p', 'p', 'o', 'r', 't', 'e', 'd',
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x00, 0x01,

	uint8(TagRange),
	0x00, 0x00, // No name
	0x00, 0x08, // Value length + value
	0x00, 0x00, 0x00, 0x10,
	0x00, 0x00, 0x00, 0x20,

	uint8(TagEnd),
}

// The bad message - 1
//
// This message violates IPP encoding rules: instead of
// using TagMemberName it uses named attributes
//
// It must not decode normally, but must decode with workarounds
var badMessage1 = []byte{
	0x01, 0x01, // IPP version */
	0x00, 0x02, // Print-Job operation */
	0x00, 0x00, 0x00, 0x01, // Request ID */

	uint8(TagOperationGroup),

	uint8(TagCharset),

	0x00, 0x12, // Name length + name
	'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
	'c', 'h', 'a', 'r', 's', 'e', 't',
	0x00, 0x05, // Value length + value
	'u', 't', 'f', '-', '8',

	uint8(TagLanguage),
	0x00, 0x1b, // Name length + name
	'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
	'n', 'a', 't', 'u', 'r', 'a', 'l', '-', 'l', 'a', 'n',
	'g', 'u', 'a', 'g', 'e',
	0x00, 0x02, // Value length + value
	'e', 'n',

	uint8(TagURI),
	0x00, 0x0b, // Name length + name
	'p', 'r', 'i', 'n', 't', 'e', 'r', '-', 'u', 'r', 'i',
	0x00, 0x1c, // Value length + value
	'i', 'p', 'p', ':', '/', '/', 'l', 'o', 'c', 'a', 'l',
	'h', 'o', 's', 't', '/', 'p', 'r', 'i', 'n', 't', 'e',
	'r', 's', '/', 'f', 'o', 'o',

	uint8(TagJobGroup),

	uint8(TagBeginCollection),
	0x00, 0x09, // Name length + name
	'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
	0x00, 0x00, // No value

	uint8(TagBeginCollection),
	0x00, 0x0a, // Name length + name
	'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
	0x00, 0x00, // No value

	uint8(TagInteger),
	0x00, 0x0b, // Name length + name
	'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x54, 0x56,

	uint8(TagInteger),
	0x00, 0x0b, // Name length + name
	'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
	0x00, 0x04, // Value length + value
	0x00, 0x00, 0x6d, 0x24,

	uint8(TagEndCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagEndCollection),
	0x00, 0x00, // No name
	0x00, 0x00, // No value

	uint8(TagEnd),
}

// The big real example, Get-Printer-Attributes output
// from the HP OfficeJet Pro 8730
var attrsHPOfficeJetPro8730 = []byte{
	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x47, 0x00, 0x12, 0x61, 0x74, 0x74, 0x72,
	0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05,
	0x75, 0x74, 0x66, 0x2d, 0x38, 0x48, 0x00, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
	0x65, 0x73, 0x2d, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75,
	0x61, 0x67, 0x65, 0x00, 0x02, 0x65, 0x6e, 0x04, 0x45, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x75, 0x72, 0x69, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x19, 0x69, 0x70, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
	0x74, 0x2f, 0x69, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x45, 0x00, 0x00, 0x00, 0x1e,
	0x69, 0x70, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
	0x3a, 0x34, 0x34, 0x33, 0x2f, 0x69, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x44, 0x00,
	0x16, 0x75, 0x72, 0x69, 0x2d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2d, 0x73, 0x75,
	0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x44, 0x00, 0x00,
	0x00, 0x03, 0x74, 0x6c, 0x73, 0x44, 0x00, 0x1c, 0x75, 0x72, 0x69, 0x2d, 0x61, 0x75, 0x74, 0x68,
	0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
	0x72, 0x74, 0x65, 0x64, 0x00, 0x14, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
	0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x14, 0x72,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6e,
	0x61, 0x6d, 0x65, 0x44, 0x00, 0x25, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x65,
	0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
	0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x11, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d, 0x73, 0x73, 0x69, 0x64, 0x44, 0x00,
	0x00, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d,
	0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d, 0x73, 0x73, 0x69, 0x64, 0x00, 0x09, 0x41, 0x31,
	0x2d, 0x35, 0x45, 0x46, 0x33, 0x37, 0x31, 0x23, 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
	0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x00, 0x04, 0x00, 0x00,
	0x00, 0x04, 0x42, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d,
	0x65, 0x00, 0x08, 0x48, 0x50, 0x30, 0x38, 0x43, 0x32, 0x32, 0x39, 0x41, 0x00, 0x10, 0x70, 0x72,
	0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
	0x45, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x6f, 0x72, 0x65, 0x2d,
	0x69, 0x6e, 0x66, 0x6f, 0x00, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63,
	0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x23, 0x68, 0x49, 0x64, 0x2d, 0x70, 0x67, 0x41, 0x69,
	0x72, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x41, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
	0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x1e, 0x48, 0x50, 0x20, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65,
	0x4a, 0x65, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37, 0x33, 0x30, 0x20, 0x5b, 0x30, 0x38,
	0x43, 0x32, 0x32, 0x39, 0x5d, 0x42, 0x00, 0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
	0x64, 0x6e, 0x73, 0x2d, 0x73, 0x64, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x1e, 0x48, 0x50, 0x20,
	0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x4a, 0x65, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37,
	0x33, 0x30, 0x20, 0x5b, 0x30, 0x38, 0x43, 0x32, 0x32, 0x39, 0x5d, 0x41, 0x00, 0x16, 0x70, 0x72,
	0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d,
	0x6f, 0x64, 0x65, 0x6c, 0x00, 0x15, 0x48, 0x50, 0x20, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x4a,
	0x65, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37, 0x33, 0x30, 0x23, 0x00, 0x0d, 0x70, 0x72,
	0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00,
	0x03, 0x44, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74,
	0x65, 0x2d, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x00, 0x1a, 0x77, 0x69, 0x66, 0x69, 0x2d,
	0x6e, 0x6f, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x2d, 0x72,
	0x65, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
	0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00, 0x44,
	0x00, 0x16, 0x69, 0x70, 0x70, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73,
	0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2e, 0x30, 0x44, 0x00, 0x00,
	0x00, 0x03, 0x31, 0x2e, 0x31, 0x44, 0x00, 0x00, 0x00, 0x03, 0x32, 0x2e, 0x30, 0x23, 0x00, 0x14,
	0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
	0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x23, 0x00, 0x00, 0x00,
	0x04, 0x00, 0x00, 0x00, 0x39, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x23, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
	0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x00, 0x06, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x23, 0x00, 0x00, 0x00, 0x04,
	0x00, 0x00, 0x40, 0x29, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x2a, 0x23, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x23,
	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3b, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
	0x3c, 0x47, 0x00, 0x12, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66,
	0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x47, 0x00, 0x11,
	0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x48, 0x00, 0x1b, 0x6e, 0x61, 0x74, 0x75, 0x72,
	0x61, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x66,
	0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x00, 0x02, 0x65, 0x6e, 0x48, 0x00, 0x24, 0x67, 0x65, 0x6e,
	0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x2d, 0x6c,
	0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x02, 0x65, 0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x66, 0x72, 0x48, 0x00, 0x00, 0x00,
	0x02, 0x64, 0x65, 0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x73, 0x48, 0x00, 0x00, 0x00, 0x02, 0x69,
	0x74, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73, 0x76, 0x48, 0x00, 0x00, 0x00, 0x02, 0x64, 0x61, 0x48,
	0x00, 0x00, 0x00, 0x02, 0x6e, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6e, 0x6c, 0x48, 0x00, 0x00,
	0x00, 0x02, 0x66, 0x69, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6a, 0x61, 0x48, 0x00, 0x00, 0x00, 0x02,
	0x70, 0x74, 0x48, 0x00, 0x00, 0x00, 0x02, 0x70, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02, 0x74, 0x72,
	0x48, 0x00, 0x00, 0x00, 0x05, 0x7a, 0x68, 0x2d, 0x74, 0x77, 0x48, 0x00, 0x00, 0x00, 0x05, 0x7a,
	0x68, 0x2d, 0x63, 0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02,
	0x63, 0x73, 0x48, 0x00, 0x00, 0x00, 0x02, 0x68, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6b, 0x6f,
	0x48, 0x00, 0x00, 0x00, 0x02, 0x68, 0x65, 0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x6c, 0x48, 0x00,
	0x00, 0x00, 0x02, 0x61, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x62, 0x67, 0x48, 0x00, 0x00, 0x00,
	0x02, 0x68, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73,
	0x6b, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73, 0x6c, 0x48, 0x00, 0x23, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75,
	0x61, 0x67, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x02,
	0x65, 0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x66, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x64, 0x65,
	0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x73, 0x48, 0x00, 0x00, 0x00, 0x02, 0x69, 0x74, 0x48, 0x00,
	0x00, 0x00, 0x02, 0x73, 0x76, 0x48, 0x00, 0x00, 0x00, 0x02, 0x64, 0x61, 0x48, 0x00, 0x00, 0x00,
	0x02, 0x6e, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6e, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02, 0x66,
	0x69, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6a, 0x61, 0x48, 0x00, 0x00, 0x00, 0x02, 0x70, 0x74, 0x48,
	0x00, 0x00, 0x00, 0x02, 0x70, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02, 0x74, 0x72, 0x48, 0x00, 0x00,
	0x00, 0x05, 0x7a, 0x68, 0x2d, 0x74, 0x77, 0x48, 0x00, 0x00, 0x00, 0x05, 0x7a, 0x68, 0x2d, 0x63,
	0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02, 0x63, 0x73, 0x48,
	0x00, 0x00, 0x00, 0x02, 0x68, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6b, 0x6f, 0x48, 0x00, 0x00,
	0x00, 0x02, 0x68, 0x65, 0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02,
	0x61, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x62, 0x67, 0x48, 0x00, 0x00, 0x00, 0x02, 0x68, 0x72,
	0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73, 0x6b, 0x48, 0x00,
	0x00, 0x00, 0x02, 0x73, 0x6c, 0x45, 0x00, 0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
	0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x75, 0x72, 0x69, 0x00, 0x25, 0x68, 0x74, 0x74,
	0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x69, 0x70,
	0x70, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x65, 0x6e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e,
	0x67, 0x73, 0x49, 0x00, 0x17, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f,
	0x72, 0x6d, 0x61, 0x74, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x18, 0x61, 0x70,
	0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d,
	0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x00, 0x19, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
	0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
	0x65, 0x64, 0x00, 0x16, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f,
	0x76, 0x6e, 0x64, 0x2e, 0x68, 0x70, 0x2d, 0x50, 0x43, 0x4c, 0x49, 0x00, 0x00, 0x00, 0x18, 0x61,
	0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x68,
	0x70, 0x2d, 0x50, 0x43, 0x4c, 0x58, 0x4c, 0x49, 0x00, 0x00, 0x00, 0x16, 0x61, 0x70, 0x70, 0x6c,
	0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x73, 0x63, 0x72, 0x69,
	0x70, 0x74, 0x49, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
	0x6f, 0x6e, 0x2f, 0x70, 0x64, 0x66, 0x49, 0x00, 0x00, 0x00, 0x0a, 0x69, 0x6d, 0x61, 0x67, 0x65,
	0x2f, 0x6a, 0x70, 0x65, 0x67, 0x49, 0x00, 0x00, 0x00, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x50, 0x43, 0x4c, 0x6d, 0x49, 0x00, 0x00, 0x00, 0x09, 0x69,
	0x6d, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x72, 0x66, 0x49, 0x00, 0x00, 0x00, 0x10, 0x69, 0x6d, 0x61,
	0x67, 0x65, 0x2f, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73, 0x74, 0x65, 0x72, 0x49, 0x00, 0x00,
	0x00, 0x18, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63,
	0x74, 0x65, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x00, 0x21, 0x64, 0x6f, 0x63,
	0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2d, 0x76, 0x65, 0x72,
	0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x05,
	0x50, 0x43, 0x4c, 0x35, 0x63, 0x41, 0x00, 0x00, 0x00, 0x05, 0x50, 0x43, 0x4c, 0x58, 0x4c, 0x41,
	0x00, 0x00, 0x00, 0x04, 0x50, 0x53, 0x2f, 0x33, 0x41, 0x00, 0x00, 0x00, 0x0c, 0x4e, 0x41, 0x54,
	0x49, 0x56, 0x45, 0x4f, 0x46, 0x46, 0x49, 0x43, 0x45, 0x41, 0x00, 0x00, 0x00, 0x07, 0x50, 0x44,
	0x46, 0x2f, 0x31, 0x2e, 0x37, 0x41, 0x00, 0x00, 0x00, 0x07, 0x50, 0x43, 0x4c, 0x33, 0x47, 0x55,
	0x49, 0x41, 0x00, 0x00, 0x00, 0x04, 0x50, 0x43, 0x4c, 0x33, 0x41, 0x00, 0x00, 0x00, 0x03, 0x50,
	0x4a, 0x4c, 0x41, 0x00, 0x00, 0x00, 0x09, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63,
	0x41, 0x00, 0x00, 0x00, 0x04, 0x4a, 0x50, 0x45, 0x47, 0x41, 0x00, 0x00, 0x00, 0x04, 0x50, 0x43,
	0x4c, 0x4d, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x52, 0x61, 0x73, 0x74,
	0x65, 0x72, 0x41, 0x00, 0x00, 0x00, 0x09, 0x50, 0x57, 0x47, 0x52, 0x61, 0x73, 0x74, 0x65, 0x72,
	0x22, 0x00, 0x19, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x73, 0x2d, 0x61, 0x63,
	0x63, 0x65, 0x70, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x6a, 0x6f, 0x62, 0x73, 0x00, 0x01, 0x01, 0x21,
	0x00, 0x10, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x2d, 0x6a, 0x6f, 0x62, 0x2d, 0x63, 0x6f, 0x75,
	0x6e, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x16, 0x70, 0x64, 0x6c, 0x2d, 0x6f,
	0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x21, 0x00, 0x0f, 0x70,
	0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x70, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x04,
	0x00, 0x00, 0x07, 0xdf, 0x31, 0x00, 0x14, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x63,
	0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x0b, 0x07, 0xe4, 0x01,
	0x13, 0x0b, 0x17, 0x17, 0x00, 0x2b, 0x00, 0x00, 0x44, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x70, 0x72,
	0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x44, 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x6c, 0x61,
	0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x22, 0x00, 0x0f, 0x63, 0x6f,
	0x6c, 0x6f, 0x72, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x01,
	0x44, 0x00, 0x21, 0x6a, 0x6f, 0x62, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
	0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
	0x72, 0x74, 0x65, 0x64, 0x00, 0x06, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00,
	0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x00, 0x00, 0x00, 0x0e,
	0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x44, 0x00,
	0x00, 0x00, 0x11, 0x6a, 0x6f, 0x62, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x70, 0x65, 0x72,
	0x2d, 0x73, 0x65, 0x74, 0x44, 0x00, 0x00, 0x00, 0x05, 0x73, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00,
	0x00, 0x00, 0x15, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x72,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x44, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64,
	0x69, 0x61, 0x44, 0x00, 0x00, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x71, 0x75, 0x61,
	0x6c, 0x69, 0x74, 0x79, 0x44, 0x00, 0x00, 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
	0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0a,
	0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x62, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x09, 0x6d,
	0x65, 0x64, 0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x44, 0x00, 0x00, 0x00, 0x0b, 0x6f, 0x75, 0x74,
	0x70, 0x75, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x44, 0x00, 0x00, 0x00, 0x16, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d,
	0x69, 0x7a, 0x65, 0x44, 0x00, 0x00, 0x00, 0x16, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x6f, 0x75,
	0x72, 0x63, 0x65, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x00,
	0x00, 0x00, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d, 0x6d,
	0x6f, 0x64, 0x65, 0x44, 0x00, 0x00, 0x00, 0x16, 0x69, 0x70, 0x70, 0x2d, 0x61, 0x74, 0x74, 0x72,
	0x69, 0x62, 0x75, 0x74, 0x65, 0x2d, 0x66, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x74, 0x79, 0x44, 0x00,
	0x00, 0x00, 0x08, 0x6a, 0x6f, 0x62, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0b,
	0x70, 0x61, 0x67, 0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x1a,
	0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
	0x74, 0x2d, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6a,
	0x6f, 0x62, 0x2d, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2d, 0x61, 0x74, 0x74,
	0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x09, 0x6f, 0x76, 0x65, 0x72,
	0x72, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x16, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d,
	0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74,
	0x44, 0x00, 0x00, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x61, 0x6c, 0x69,
	0x6e, 0x67, 0x46, 0x00, 0x1f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2d, 0x75,
	0x72, 0x69, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
	0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x68, 0x74, 0x74, 0x70, 0x46, 0x00, 0x00, 0x00, 0x05, 0x68,
	0x74, 0x74, 0x70, 0x73, 0x41, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x64,
	0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x01, 0x99, 0x4d, 0x46, 0x47, 0x3a, 0x48, 0x50,
	0x3b, 0x4d, 0x44, 0x4c, 0x3a, 0x48, 0x50, 0x20, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x4a, 0x65,
	0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37, 0x33, 0x30, 0x3b, 0x43, 0x4d, 0x44, 0x3a, 0x50,
	0x43, 0x4c, 0x35, 0x63, 0x2c, 0x50, 0x43, 0x4c, 0x58, 0x4c, 0x2c, 0x50, 0x4f, 0x53, 0x54, 0x53,
	0x43, 0x52, 0x49, 0x50, 0x54, 0x2c, 0x4e, 0x41, 0x54, 0x49, 0x56, 0x45, 0x4f, 0x46, 0x46, 0x49,
	0x43, 0x45, 0x2c, 0x50, 0x44, 0x46, 0x2c, 0x50, 0x43, 0x4c, 0x33, 0x47, 0x55, 0x49, 0x2c, 0x50,
	0x43, 0x4c, 0x33, 0x2c, 0x50, 0x4a, 0x4c, 0x2c, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69,
	0x63, 0x2c, 0x4a, 0x50, 0x45, 0x47, 0x2c, 0x50, 0x43, 0x4c, 0x4d, 0x2c, 0x41, 0x70, 0x70, 0x6c,
	0x65, 0x52, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2c, 0x50, 0x57, 0x47, 0x52, 0x61, 0x73, 0x74, 0x65,
	0x72, 0x2c, 0x38, 0x30, 0x32, 0x2e, 0x31, 0x31, 0x2c, 0x38, 0x30, 0x32, 0x2e, 0x33, 0x2c, 0x44,
	0x45, 0x53, 0x4b, 0x4a, 0x45, 0x54, 0x2c, 0x44, 0x59, 0x4e, 0x3b, 0x43, 0x4c, 0x53, 0x3a, 0x50,
	0x52, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x3b, 0x44, 0x45, 0x53, 0x3a, 0x44, 0x39, 0x4c, 0x32, 0x30,
	0x41, 0x3b, 0x43, 0x49, 0x44, 0x3a, 0x48, 0x50, 0x49, 0x4a, 0x56, 0x49, 0x50, 0x41, 0x56, 0x39,
	0x3b, 0x4c, 0x45, 0x44, 0x4d, 0x44, 0x49, 0x53, 0x3a, 0x55, 0x53, 0x42, 0x23, 0x46, 0x46, 0x23,
	0x43, 0x43, 0x23, 0x30, 0x30, 0x2c, 0x55, 0x53, 0x42, 0x23, 0x46, 0x46, 0x23, 0x30, 0x34, 0x23,
	0x30, 0x31, 0x3b, 0x4d, 0x43, 0x54, 0x3a, 0x4d, 0x46, 0x3b, 0x4d, 0x43, 0x4c, 0x3a, 0x44, 0x49,
	0x3b, 0x4d, 0x43, 0x56, 0x3a, 0x33, 0x2e, 0x30, 0x3b, 0x53, 0x4e, 0x3a, 0x43, 0x4e, 0x37, 0x38,
	0x33, 0x46, 0x36, 0x30, 0x57, 0x31, 0x3b, 0x53, 0x3a, 0x30, 0x33, 0x38, 0x30, 0x38, 0x30, 0x43,
	0x34, 0x38, 0x34, 0x31, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x35, 0x38, 0x30, 0x30, 0x38,
	0x30, 0x30, 0x30, 0x30, 0x34, 0x34, 0x31, 0x38, 0x30, 0x30, 0x33, 0x63, 0x34, 0x35, 0x31, 0x38,
	0x30, 0x30, 0x34, 0x36, 0x34, 0x36, 0x31, 0x38, 0x30, 0x30, 0x33, 0x63, 0x34, 0x31, 0x31, 0x38,
	0x30, 0x30, 0x34, 0x36, 0x3b, 0x5a, 0x3a, 0x30, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x30,
	0x30, 0x30, 0x30, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30,
	0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x2c, 0x31, 0x32, 0x30, 0x30, 0x30, 0x2c, 0x31, 0x37,
	0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x31,
	0x38, 0x31, 0x3b, 0x30, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x61, 0x6c,
	0x65, 0x72, 0x74, 0x00, 0x27, 0x63, 0x6f, 0x64, 0x65, 0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77,
	0x6e, 0x3b, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72,
	0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x30, 0x00, 0x00, 0x00,
	0x27, 0x63, 0x6f, 0x64, 0x65, 0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x73, 0x65,
	0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x3b, 0x67, 0x72, 0x6f,
	0x75, 0x70, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x30, 0x00, 0x00, 0x00, 0x27, 0x63, 0x6f, 0x64,
	0x65, 0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69,
	0x74, 0x79, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x3d, 0x6f,
	0x74, 0x68, 0x65, 0x72, 0x30, 0x00, 0x00, 0x00, 0x27, 0x63, 0x6f, 0x64, 0x65, 0x3d, 0x75, 0x6e,
	0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x3d, 0x6f,
	0x74, 0x68, 0x65, 0x72, 0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72,
	0x41, 0x00, 0x19, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x61, 0x6c, 0x65, 0x72, 0x74,
	0x2d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x09, 0x67, 0x65,
	0x6e, 0x75, 0x69, 0x6e, 0x65, 0x48, 0x50, 0x41, 0x00, 0x00, 0x00, 0x09, 0x67, 0x65, 0x6e, 0x75,
	0x69, 0x6e, 0x65, 0x48, 0x50, 0x41, 0x00, 0x00, 0x00, 0x09, 0x67, 0x65, 0x6e, 0x75, 0x69, 0x6e,
	0x65, 0x48, 0x50, 0x41, 0x00, 0x00, 0x00, 0x09, 0x67, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x48,
	0x50, 0x45, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x75, 0x69, 0x64,
	0x00, 0x2d, 0x75, 0x72, 0x6e, 0x3a, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x64, 0x62, 0x63, 0x63, 0x34,
	0x62, 0x35, 0x38, 0x2d, 0x66, 0x63, 0x34, 0x63, 0x2d, 0x66, 0x36, 0x66, 0x64, 0x2d, 0x62, 0x34,
	0x64, 0x36, 0x2d, 0x32, 0x62, 0x30, 0x30, 0x64, 0x35, 0x35, 0x34, 0x61, 0x64, 0x34, 0x37, 0x23,
	0x00, 0x29, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2d, 0x6f, 0x72, 0x69, 0x65,
	0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,
	0x64, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00,
	0x05, 0x34, 0x00, 0x19, 0x6a, 0x6f, 0x62, 0x2d, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69,
	0x6e, 0x74, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x00, 0x4a,
	0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d,
	0x65, 0x42, 0x00, 0x00, 0x00, 0x0f, 0x66, 0x75, 0x6c, 0x6c, 0x62, 0x6c, 0x65, 0x65, 0x64, 0x2d,
	0x73, 0x69, 0x7a, 0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
	0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f,
	0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
	0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c,
	0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69,
	0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44, 0x00, 0x00,
	0x00, 0x14, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x35, 0x2e, 0x35,
	0x78, 0x38, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e,
	0x64, 0x65, 0x78, 0x2d, 0x35, 0x78, 0x38, 0x5f, 0x35, 0x78, 0x38, 0x69, 0x6e, 0x44, 0x00, 0x00,
	0x00, 0x14, 0x6e, 0x61, 0x5f, 0x6f, 0x66, 0x69, 0x63, 0x69, 0x6f, 0x5f, 0x38, 0x2e, 0x35, 0x78,
	0x31, 0x33, 0x2e, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x6f, 0x6d, 0x5f, 0x31, 0x36,
	0x6b, 0x5f, 0x31, 0x39, 0x35, 0x78, 0x32, 0x37, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10,
	0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x38, 0x34, 0x78, 0x32, 0x36, 0x30, 0x6d, 0x6d,
	0x44, 0x00, 0x00, 0x00, 0x14, 0x72, 0x6f, 0x63, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x37, 0x2e, 0x37,
	0x35, 0x78, 0x31, 0x30, 0x2e, 0x37, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6a, 0x70,
	0x6e, 0x5f, 0x6f, 0x75, 0x66, 0x75, 0x6b, 0x75, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x30, 0x30,
	0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
	0x2d, 0x31, 0x30, 0x5f, 0x34, 0x2e, 0x31, 0x32, 0x35, 0x78, 0x39, 0x2e, 0x35, 0x69, 0x6e, 0x44,
	0x00, 0x00, 0x00, 0x16, 0x6e, 0x61, 0x5f, 0x6d, 0x6f, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x33,
	0x2e, 0x38, 0x37, 0x35, 0x78, 0x37, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69,
	0x73, 0x6f, 0x5f, 0x62, 0x35, 0x5f, 0x31, 0x37, 0x36, 0x78, 0x32, 0x35, 0x30, 0x6d, 0x6d, 0x44,
	0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x35, 0x5f, 0x31, 0x36, 0x32, 0x78, 0x32,
	0x32, 0x39, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x36, 0x5f,
	0x31, 0x31, 0x34, 0x78, 0x31, 0x36, 0x32, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73,
	0x6f, 0x5f, 0x64, 0x6c, 0x5f, 0x31, 0x31, 0x30, 0x78, 0x32, 0x32, 0x30, 0x6d, 0x6d, 0x44, 0x00,
	0x00, 0x00, 0x13, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f, 0x75, 0x33, 0x5f, 0x31, 0x32, 0x30,
	0x78, 0x32, 0x33, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6a, 0x70, 0x6e, 0x5f, 0x63,
	0x68, 0x6f, 0x75, 0x34, 0x5f, 0x39, 0x30, 0x78, 0x32, 0x30, 0x35, 0x6d, 0x6d, 0x37, 0x00, 0x00,
	0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f,
	0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x75,
	0x70, 0x6c, 0x65, 0x78, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x73,
	0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64,
	0x65, 0x64, 0x2d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00, 0x00,
	0x00, 0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d, 0x6c, 0x6f, 0x6e, 0x67,
	0x2d, 0x65, 0x64, 0x67, 0x65, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44,
	0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x38, 0x2e,
	0x35, 0x78, 0x31, 0x31, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x11, 0x6e, 0x61, 0x5f, 0x6c, 0x65,
	0x67, 0x61, 0x6c, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00,
	0x18, 0x6e, 0x61, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x37, 0x2e,
	0x32, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6e, 0x61,
	0x5f, 0x66, 0x6f, 0x6f, 0x6c, 0x73, 0x63, 0x61, 0x70, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x33,
	0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x34, 0x5f, 0x32, 0x31,
	0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x6a, 0x69, 0x73, 0x5f,
	0x62, 0x35, 0x5f, 0x31, 0x38, 0x32, 0x78, 0x32, 0x35, 0x37, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00,
	0x14, 0x6e, 0x61, 0x5f, 0x6f, 0x66, 0x69, 0x63, 0x69, 0x6f, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31,
	0x33, 0x2e, 0x34, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
	0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e,
	0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x2d, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x44, 0x00,
	0x00, 0x00, 0x09, 0x63, 0x61, 0x72, 0x64, 0x73, 0x74, 0x6f, 0x63, 0x6b, 0x37, 0x00, 0x00, 0x00,
	0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c,
	0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x75, 0x70,
	0x6c, 0x65, 0x78, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x73, 0x69,
	0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65,
	0x64, 0x2d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00, 0x00, 0x00,
	0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d, 0x6c, 0x6f, 0x6e, 0x67, 0x2d,
	0x65, 0x64, 0x67, 0x65, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44, 0x00,
	0x00, 0x00, 0x14, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x35, 0x2e,
	0x35, 0x78, 0x38, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69,
	0x6e, 0x64, 0x65, 0x78, 0x2d, 0x33, 0x78, 0x35, 0x5f, 0x33, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00,
	0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x34, 0x78, 0x36, 0x5f,
	0x34, 0x78, 0x36, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x61, 0x5f, 0x35, 0x78, 0x37,
	0x5f, 0x35, 0x78, 0x37, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e,
	0x64, 0x65, 0x78, 0x2d, 0x35, 0x78, 0x38, 0x5f, 0x35, 0x78, 0x38, 0x69, 0x6e, 0x44, 0x00, 0x00,
	0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x35, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x31, 0x30,
	0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x36, 0x5f, 0x31, 0x30,
	0x35, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6f, 0x65, 0x5f, 0x70,
	0x68, 0x6f, 0x74, 0x6f, 0x2d, 0x6c, 0x5f, 0x33, 0x2e, 0x35, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00,
	0x00, 0x00, 0x18, 0x6f, 0x6d, 0x5f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x2d, 0x70, 0x68, 0x6f, 0x74,
	0x6f, 0x5f, 0x31, 0x30, 0x30, 0x78, 0x31, 0x35, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10,
	0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x39, 0x35, 0x78, 0x32, 0x37, 0x30, 0x6d, 0x6d,
	0x44, 0x00, 0x00, 0x00, 0x10, 0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x38, 0x34, 0x78,
	0x32, 0x36, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x72, 0x6f, 0x63, 0x5f, 0x31, 0x36,
	0x6b, 0x5f, 0x37, 0x2e, 0x37, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x37, 0x35, 0x69, 0x6e, 0x44, 0x00,
	0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f, 0x68, 0x61, 0x67, 0x61, 0x6b, 0x69, 0x5f, 0x31, 0x30,
	0x30, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f,
	0x6f, 0x75, 0x66, 0x75, 0x6b, 0x75, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x30, 0x30, 0x6d, 0x6d,
	0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d, 0x31,
	0x30, 0x5f, 0x34, 0x2e, 0x31, 0x32, 0x35, 0x78, 0x39, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00,
	0x00, 0x16, 0x6e, 0x61, 0x5f, 0x6d, 0x6f, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x33, 0x2e, 0x38,
	0x37, 0x35, 0x78, 0x37, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f,
	0x5f, 0x62, 0x35, 0x5f, 0x31, 0x37, 0x36, 0x78, 0x32, 0x35, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00,
	0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x35, 0x5f, 0x31, 0x36, 0x32, 0x78, 0x32, 0x32, 0x39,
	0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x36, 0x5f, 0x31, 0x31,
	0x34, 0x78, 0x31, 0x36, 0x32, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f,
	0x64, 0x6c, 0x5f, 0x31, 0x31, 0x30, 0x78, 0x32, 0x32, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00,
	0x13, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f, 0x75, 0x33, 0x5f, 0x31, 0x32, 0x30, 0x78, 0x32,
	0x33, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f,
	0x75, 0x34, 0x5f, 0x39, 0x30, 0x78, 0x32, 0x30, 0x35, 0x6d, 0x6d, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x17, 0x6a, 0x6f, 0x62, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x73,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
	0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00,
	0x00, 0x00, 0x0f, 0x66, 0x75, 0x6c, 0x6c, 0x62, 0x6c, 0x65, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x7a,
	0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70,
	0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
	0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f,
	0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01,
	0x28, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74,
	0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
	0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74,
	0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
	0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0d, 0x72,
	0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00, 0x00, 0x00,
	0x0c, 0x64, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x4a, 0x00, 0x00,
	0x00, 0x05, 0x73, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x09, 0x6f, 0x6e, 0x65, 0x2d,
	0x73, 0x69, 0x64, 0x65, 0x64, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a,
	0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d,
	0x65, 0x42, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x2d, 0x73, 0x69, 0x7a,
	0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x73, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00,
	0x09, 0x6f, 0x6e, 0x65, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x37, 0x00, 0x00, 0x00, 0x00, 0x44,
	0x00, 0x16, 0x69, 0x70, 0x70, 0x2d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2d, 0x73,
	0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x0c, 0x61, 0x69, 0x72, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x2d, 0x31, 0x2e, 0x35, 0x44, 0x00, 0x00, 0x00, 0x0e, 0x69, 0x70, 0x70, 0x2d, 0x65,
	0x76, 0x65, 0x72, 0x79, 0x77, 0x68, 0x65, 0x72, 0x65, 0x44, 0x00, 0x14, 0x77, 0x68, 0x69, 0x63,
	0x68, 0x2d, 0x6a, 0x6f, 0x62, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x44, 0x00, 0x00, 0x00, 0x0d,
	0x6e, 0x6f, 0x74, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x44, 0x00, 0x00,
	0x00, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x00, 0x11, 0x6a, 0x6f, 0x62, 0x2d, 0x69, 0x64, 0x73, 0x2d,
	0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x01, 0x22, 0x00, 0x1d, 0x72,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x75,
	0x72, 0x69, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x01, 0x44,
	0x00, 0x22, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61,
	0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x6f, 0x75, 0x74, 0x2d, 0x61, 0x63,
	0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x6a, 0x6f,
	0x62, 0x12, 0x00, 0x14, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x67, 0x65, 0x6f, 0x2d,
	0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x41, 0x00, 0x10, 0x6d, 0x6f, 0x70,
	0x72, 0x69, 0x61, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x00, 0x03, 0x31,
	0x2e, 0x33, 0x22, 0x00, 0x1e, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x2d, 0x61,
	0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
	0x74, 0x65, 0x64, 0x00, 0x01, 0x00, 0x31, 0x00, 0x1f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
	0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x64,
	0x61, 0x74, 0x65, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x0b, 0x07, 0xe4, 0x01, 0x13, 0x0a, 0x31,
	0x10, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x00, 0x1a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x74, 0x69,
	0x6d, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x1e, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d,
	0x64, 0x61, 0x74, 0x65, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x0b, 0x07, 0xe4, 0x01, 0x13, 0x0a,
	0x31, 0x10, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x00, 0x19, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
	0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x74, 0x69,
	0x6d, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x1b, 0x64, 0x6f, 0x63, 0x75, 0x6d,
	0x65, 0x6e, 0x74, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2d, 0x73, 0x75, 0x70,
	0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0c, 0x70,
	0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x00, 0x08, 0x64, 0x6f, 0x63,
	0x75, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x00, 0x00, 0x00, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
	0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00,
	0x08, 0x70, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x72, 0x64, 0x44, 0x00, 0x0f, 0x6d, 0x65, 0x64, 0x69,
	0x61, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x12, 0x6e, 0x61, 0x5f,
	0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x31, 0x69, 0x6e, 0x44,
	0x00, 0x00, 0x00, 0x11, 0x6e, 0x61, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x5f, 0x38, 0x2e, 0x35,
	0x78, 0x31, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x65, 0x78, 0x65,
	0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x37, 0x2e, 0x32, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x35,
	0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63,
	0x65, 0x5f, 0x35, 0x2e, 0x35, 0x78, 0x38, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14,
	0x6e, 0x61, 0x5f, 0x66, 0x6f, 0x6f, 0x6c, 0x73, 0x63, 0x61, 0x70, 0x5f, 0x38, 0x2e, 0x35, 0x78,
	0x31, 0x33, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65,
	0x78, 0x2d, 0x33, 0x78, 0x35, 0x5f, 0x33, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12,
	0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x34, 0x78, 0x36, 0x5f, 0x34, 0x78, 0x36,
	0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x61, 0x5f, 0x35, 0x78, 0x37, 0x5f, 0x35, 0x78,
	0x37, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
	0x2d, 0x35, 0x78, 0x38, 0x5f, 0x35, 0x78, 0x38, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69,
	0x73, 0x6f, 0x5f, 0x61, 0x34, 0x5f, 0x32, 0x31, 0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x44,
	0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x35, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32,
	0x31, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x36, 0x5f,
	0x31, 0x30, 0x35, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x6a, 0x69,
	0x73, 0x5f, 0x62, 0x35, 0x5f, 0x31, 0x38, 0x32, 0x78, 0x32, 0x35, 0x37, 0x6d, 0x6d, 0x44, 0x00,
	0x00, 0x00, 0x12, 0x6f, 0x65, 0x5f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x2d, 0x6c, 0x5f, 0x33, 0x2e,
	0x35, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6f, 0x6d, 0x5f, 0x73, 0x6d, 0x61,
	0x6c, 0x6c, 0x2d, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x5f, 0x31, 0x30, 0x30, 0x78, 0x31, 0x35, 0x30,
	0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6e, 0x61, 0x5f, 0x6f, 0x66, 0x69, 0x63, 0x69, 0x6f,
	0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x33, 0x2e, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10,
	0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x39, 0x35, 0x78, 0x32, 0x37, 0x30, 0x6d, 0x6d,
	0x44, 0x00, 0x00, 0x00, 0x10, 0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x38, 0x34, 0x78,
	0x32, 0x36, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x72, 0x6f, 0x63, 0x5f, 0x31, 0x36,
	0x6b, 0x5f, 0x37, 0x2e, 0x37, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x37, 0x35, 0x69, 0x6e, 0x44, 0x00,
	0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f, 0x68, 0x61, 0x67, 0x61, 0x6b, 0x69, 0x5f, 0x31, 0x30,
	0x30, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f,
	0x6f, 0x75, 0x66, 0x75, 0x6b, 0x75, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x30, 0x30, 0x6d, 0x6d,
	0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d, 0x31,
	0x30, 0x5f, 0x34, 0x2e, 0x31, 0x32, 0x35, 0x78, 0x39, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00,
	0x00, 0x16, 0x6e, 0x61, 0x5f, 0x6d, 0x6f, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x33, 0x2e, 0x38,
	0x37, 0x35, 0x78, 0x37, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f,
	0x5f, 0x62, 0x35, 0x5f, 0x31, 0x37, 0x36, 0x78, 0x32, 0x35, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00,
	0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x35, 0x5f, 0x31, 0x36, 0x32, 0x78, 0x32, 0x32, 0x39,
	0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x36, 0x5f, 0x31, 0x31,
	0x34, 0x78, 0x31, 0x36, 0x32, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f,
	0x64, 0x6c, 0x5f, 0x31, 0x31, 0x30, 0x78, 0x32, 0x32, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00,
	0x13, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f, 0x75, 0x33, 0x5f, 0x31, 0x32, 0x30, 0x78, 0x32,
	0x33, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f,
	0x75, 0x34, 0x5f, 0x39, 0x30, 0x78, 0x32, 0x30, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10,
	0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x33, 0x78, 0x35, 0x69, 0x6e,
	0x44, 0x00, 0x00, 0x00, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f,
	0x38, 0x2e, 0x35, 0x78, 0x31, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x0d, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x34,
	0x5f, 0x32, 0x31, 0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x44, 0x00, 0x13, 0x6d, 0x65, 0x64,
	0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00, 0x00,
	0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x44, 0x00, 0x00, 0x00, 0x10,
	0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x44, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d,
	0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x44, 0x00, 0x00,
	0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d,
	0x61, 0x72, 0x67, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0f, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x34, 0x00, 0x11, 0x6d, 0x65, 0x64,
	0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00,
	0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34,
	0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e,
	0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x10,
	0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d,
	0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x6d,
	0x61, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79,
	0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72,
	0x79, 0x37, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x17, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69,
	0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65,
	0x00, 0x00, 0x21, 0x00, 0x1b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d,
	0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21,
	0x00, 0x1c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61,
	0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04,
	0x00, 0x00, 0x01, 0x28, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x1a,
	0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x1d, 0x6d, 0x65, 0x64, 0x69,
	0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d,
	0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x21,
	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x16, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x00, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
	0x74, 0x79, 0x70, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x0a,
	0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x44, 0x00, 0x00, 0x00, 0x14, 0x63,
	0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x65, 0x63, 0x6f, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x2d, 0x6c,
	0x69, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x70,
	0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74,
	0x69, 0x6f, 0x6e, 0x2d, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x25, 0x63, 0x6f,
	0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x74, 0x72, 0x69, 0x66, 0x6f, 0x6c, 0x64, 0x2d, 0x62, 0x72, 0x6f,
	0x63, 0x68, 0x75, 0x72, 0x65, 0x2d, 0x67, 0x6c, 0x6f, 0x73, 0x73, 0x79, 0x2d, 0x31, 0x38, 0x30,
	0x67, 0x73, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x62,
	0x72, 0x6f, 0x63, 0x68, 0x75, 0x72, 0x65, 0x2d, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x44, 0x00, 0x00,
	0x00, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x62, 0x72, 0x6f, 0x63, 0x68, 0x75, 0x72,
	0x65, 0x2d, 0x67, 0x6c, 0x6f, 0x73, 0x73, 0x79, 0x44, 0x00, 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d,
	0x2e, 0x68, 0x70, 0x2e, 0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x2d, 0x70, 0x68, 0x6f,
	0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x16, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72,
	0x79, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x44, 0x00, 0x00,
	0x00, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x09, 0x63, 0x61, 0x72, 0x64, 0x73, 0x74,
	0x6f, 0x63, 0x6b, 0x44, 0x00, 0x00, 0x00, 0x16, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65,
	0x72, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x76, 0x79, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x44, 0x00,
	0x00, 0x00, 0x15, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x2d, 0x6c, 0x65,
	0x74, 0x74, 0x65, 0x72, 0x68, 0x65, 0x61, 0x64, 0x34, 0x00, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x6d, 0x24, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8a, 0xe8, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x47, 0xef, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x68, 0x2e, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x36, 0x92, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x80, 0xfc, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x1d, 0xc4, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x27, 0xb0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3b, 0x88, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x45, 0x74, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x4f, 0x60, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x39, 0xd0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x29, 0x04, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x47, 0x18, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x64, 0x64, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x22, 0xba, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x27, 0x10, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3a, 0x98, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x84, 0xf4, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x4c, 0x2c, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x69, 0x78, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x47, 0xe0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x65, 0x90, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x4c, 0xe5, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x6a, 0xa9, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x27, 0x10, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x4e, 0x20, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x28, 0xed, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x5e, 0x42, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x26, 0x72, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x4a, 0x6a, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x44, 0xc0, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x61, 0xa8, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3f, 0x48, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x59, 0x74, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x2c, 0x88, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3f, 0x48, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x2a, 0xf8, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
	0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x55, 0xf0, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x2e, 0xe0, 0x4a, 0x00,
	0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x5b, 0xcc, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x23, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x50, 0x14, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x33, 0x00, 0x00,
	0x00, 0x08, 0x00, 0x00, 0x1d, 0xc4, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x33, 0x00, 0x00, 0x00, 0x08, 0x00,
	0x00, 0x31, 0x9c, 0x00, 0x00, 0x8a, 0xe8, 0x37, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0b, 0x6d,
	0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f,
	0x61, 0x34, 0x5f, 0x32, 0x31, 0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x34, 0x00, 0x0f, 0x6d,
	0x65, 0x64, 0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x00, 0x00,
	0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34,
	0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e,
	0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00,
	0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
	0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x10,
	0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69,
	0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d,
	0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x6d,
	0x61, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79,
	0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72,
	0x79, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0a,
	0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a,
	0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21,
	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64,
	0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x74,
	0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
	0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62,
	0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00,
	0x04, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
	0x6c, 0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
	0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72,
	0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
	0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73,
	0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x00,
	0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00,
	0x00, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x37, 0x00, 0x00, 0x00,
	0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61,
	0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78,
	0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x52, 0x08, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
	0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00, 0x00,
	0x00, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d,
	0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a,
	0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d,
	0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
	0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d,
	0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a,
	0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d,
	0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a,
	0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
	0x44, 0x00, 0x00, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65,
	0x64, 0x69, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x74, 0x61,
	0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x37, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x14, 0x66,
	0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x72, 0x65,
	0x61, 0x64, 0x79, 0x00, 0x00, 0x44, 0x00, 0x18, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e,
	0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x12, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x6d, 0x70,
	0x6c, 0x61, 0x74, 0x65, 0x30, 0x00, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66,
	0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x72, 0x00, 0x00, 0x30, 0x00, 0x1c, 0x70, 0x72, 0x69, 0x6e,
	0x74, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x64, 0x65, 0x73,
	0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x21, 0x00, 0x10, 0x70, 0x61, 0x67,
	0x65, 0x73, 0x2d, 0x70, 0x65, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x00, 0x04, 0x00,
	0x00, 0x00, 0x18, 0x21, 0x00, 0x16, 0x70, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x70, 0x65, 0x72, 0x2d,
	0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x04, 0x00, 0x00,
	0x00, 0x14, 0x33, 0x00, 0x17, 0x6a, 0x70, 0x65, 0x67, 0x2d, 0x6b, 0x2d, 0x6f, 0x63, 0x74, 0x65,
	0x74, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x33, 0x00, 0x1a, 0x6a, 0x70, 0x65, 0x67, 0x2d, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
	0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x33, 0x00, 0x1a,
	0x6a, 0x70, 0x65, 0x67, 0x2d, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01,
	0x00, 0x00, 0x40, 0x00, 0x33, 0x00, 0x16, 0x70, 0x64, 0x66, 0x2d, 0x6b, 0x2d, 0x6f, 0x63, 0x74,
	0x65, 0x74, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x33, 0x00, 0x14, 0x70, 0x64, 0x66, 0x2d, 0x73, 0x69,
	0x7a, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x00, 0x08,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x22, 0x00, 0x17, 0x70, 0x64, 0x66, 0x2d, 0x66,
	0x69, 0x74, 0x2d, 0x74, 0x6f, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75,
	0x6c, 0x74, 0x00, 0x01, 0x00, 0x22, 0x00, 0x19, 0x70, 0x64, 0x66, 0x2d, 0x66, 0x69, 0x74, 0x2d,
	0x74, 0x6f, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x01, 0x00, 0x44, 0x00, 0x16, 0x70, 0x64, 0x66, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69,
	0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x10, 0x69,
	0x73, 0x6f, 0x2d, 0x33, 0x32, 0x30, 0x30, 0x30, 0x2d, 0x31, 0x5f, 0x32, 0x30, 0x30, 0x38, 0x44,
	0x00, 0x00, 0x00, 0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x37, 0x44, 0x00, 0x00,
	0x00, 0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x36, 0x44, 0x00, 0x00, 0x00, 0x09,
	0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x35, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73,
	0x6f, 0x2d, 0x31, 0x39, 0x30, 0x30, 0x35, 0x2d, 0x31, 0x5f, 0x32, 0x30, 0x30, 0x35, 0x44, 0x00,
	0x00, 0x00, 0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x34, 0x44, 0x00, 0x00, 0x00,
	0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x33, 0x44, 0x00, 0x00, 0x00, 0x09, 0x61,
	0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x32, 0x44, 0x00, 0x0d, 0x75, 0x72, 0x66, 0x2d, 0x73,
	0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x43, 0x50, 0x31, 0x44, 0x00, 0x00,
	0x00, 0x0f, 0x4d, 0x54, 0x31, 0x2d, 0x32, 0x2d, 0x38, 0x2d, 0x39, 0x2d, 0x31, 0x30, 0x2d, 0x31,
	0x31, 0x44, 0x00, 0x00, 0x00, 0x07, 0x50, 0x51, 0x33, 0x2d, 0x34, 0x2d, 0x35, 0x44, 0x00, 0x00,
	0x00, 0x09, 0x52, 0x53, 0x33, 0x30, 0x30, 0x2d, 0x36, 0x30, 0x30, 0x44, 0x00, 0x00, 0x00, 0x06,
	0x53, 0x52, 0x47, 0x42, 0x32, 0x34, 0x44, 0x00, 0x00, 0x00, 0x04, 0x4f, 0x42, 0x31, 0x30, 0x44,
	0x00, 0x00, 0x00, 0x02, 0x57, 0x38, 0x44, 0x00, 0x00, 0x00, 0x05, 0x44, 0x45, 0x56, 0x57, 0x38,
	0x44, 0x00, 0x00, 0x00, 0x08, 0x44, 0x45, 0x56, 0x52, 0x47, 0x42, 0x32, 0x34, 0x44, 0x00, 0x00,
	0x00, 0x0a, 0x41, 0x44, 0x4f, 0x42, 0x45, 0x52, 0x47, 0x42, 0x32, 0x34, 0x44, 0x00, 0x00, 0x00,
	0x03, 0x44, 0x4d, 0x33, 0x44, 0x00, 0x00, 0x00, 0x03, 0x46, 0x4e, 0x33, 0x44, 0x00, 0x00, 0x00,
	0x05, 0x49, 0x53, 0x31, 0x2d, 0x32, 0x44, 0x00, 0x00, 0x00, 0x04, 0x56, 0x31, 0x2e, 0x34, 0x42,
	0x00, 0x0c, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x08,
	0x63, 0x79, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x00, 0x00, 0x0b, 0x6d, 0x61, 0x67,
	0x65, 0x6e, 0x74, 0x61, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x00, 0x00, 0x0a, 0x79, 0x65, 0x6c,
	0x6c, 0x6f, 0x77, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x00, 0x00, 0x09, 0x62, 0x6c, 0x61, 0x63,
	0x6b, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x0d, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x63,
	0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x00, 0x07, 0x23, 0x30, 0x30, 0x46, 0x46, 0x46, 0x46, 0x42, 0x00,
	0x00, 0x00, 0x07, 0x23, 0x46, 0x46, 0x30, 0x30, 0x46, 0x46, 0x42, 0x00, 0x00, 0x00, 0x07, 0x23,
	0x46, 0x46, 0x46, 0x46, 0x30, 0x30, 0x42, 0x00, 0x00, 0x00, 0x07, 0x23, 0x30, 0x30, 0x30, 0x30,
	0x30, 0x30, 0x44, 0x00, 0x0c, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x74, 0x79, 0x70, 0x65,
	0x73, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65,
	0x44, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64,
	0x67, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x72,
	0x69, 0x64, 0x67, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72,
	0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x21, 0x00, 0x11, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d,
	0x6c, 0x6f, 0x77, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x00, 0x01, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00, 0x12, 0x6d, 0x61,
	0x72, 0x6b, 0x65, 0x72, 0x2d, 0x68, 0x69, 0x67, 0x68, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73,
	0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x21,
	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
	0x64, 0x21, 0x00, 0x0d, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c,
	0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46,
	0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x00, 0x46, 0x30, 0x00, 0x0e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x75, 0x70,
	0x70, 0x6c, 0x79, 0x00, 0x65, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x72,
	0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69,
	0x74, 0x79, 0x3d, 0x31, 0x30, 0x30, 0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x36, 0x30, 0x3b,
	0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x54, 0x68, 0x61, 0x74,
	0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x3b, 0x75, 0x6e, 0x69, 0x74, 0x3d,
	0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x61, 0x6e, 0x74,
	0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x63, 0x79, 0x61, 0x6e, 0x3b, 0x30, 0x00, 0x00, 0x00, 0x68, 0x74,
	0x79, 0x70, 0x65, 0x3d, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65,
	0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3d, 0x31, 0x30, 0x30,
	0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x37, 0x30, 0x3b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d,
	0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x54, 0x68, 0x61, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73,
	0x75, 0x6d, 0x65, 0x64, 0x3b, 0x75, 0x6e, 0x69, 0x74, 0x3d, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e,
	0x74, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d,
	0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x3b, 0x30, 0x00, 0x00, 0x00, 0x67, 0x74, 0x79, 0x70, 0x65,
	0x3d, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x3b, 0x6d, 0x61,
	0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3d, 0x31, 0x30, 0x30, 0x3b, 0x6c, 0x65,
	0x76, 0x65, 0x6c, 0x3d, 0x36, 0x30, 0x3b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x73, 0x75, 0x70,
	0x70, 0x6c, 0x79, 0x54, 0x68, 0x61, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
	0x64, 0x3b, 0x75, 0x6e, 0x69, 0x74, 0x3d, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x3b, 0x63,
	0x6f, 0x6c, 0x6f, 0x72, 0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x79, 0x65, 0x6c, 0x6c,
	0x6f, 0x77, 0x3b, 0x30, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x69, 0x6e, 0x6b,
	0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70,
	0x61, 0x63, 0x69, 0x74, 0x79, 0x3d, 0x31, 0x30, 0x30, 0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d,
	0x37, 0x30, 0x3b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x54,
	0x68, 0x61, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x3b, 0x75, 0x6e,
	0x69, 0x74, 0x3d, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
	0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x3b, 0x41, 0x00,
	0x1a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x2d,
	0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0e, 0x43, 0x79, 0x61,
	0x6e, 0x20, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x41, 0x00, 0x00, 0x00, 0x11,
	0x4d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67,
	0x65, 0x41, 0x00, 0x00, 0x00, 0x10, 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x43, 0x61, 0x72,
	0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x41, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x6c, 0x61, 0x63, 0x6b,
	0x20, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x42, 0x00, 0x15, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x6e, 0x61,
	0x6d, 0x65, 0x00, 0x16, 0x57, 0x45, 0x42, 0x50, 0x44, 0x4c, 0x50, 0x50, 0x31, 0x4e, 0x30, 0x30,
	0x31, 0x2e, 0x31, 0x39, 0x31, 0x39, 0x41, 0x2e, 0x30, 0x30, 0x41, 0x00, 0x1f, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x73, 0x74,
	0x72, 0x69, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x16, 0x57, 0x45,
	0x42, 0x50, 0x44, 0x4c, 0x50, 0x50, 0x31, 0x4e, 0x30, 0x30, 0x31, 0x2e, 0x31, 0x39, 0x31, 0x39,
	0x41, 0x2e, 0x30, 0x30, 0x30, 0x00, 0x18, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66,
	0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00,
	0x16, 0x57, 0x45, 0x42, 0x50, 0x44, 0x4c, 0x50, 0x50, 0x31, 0x4e, 0x30, 0x30, 0x31, 0x2e, 0x31,
	0x39, 0x31, 0x39, 0x41, 0x2e, 0x30, 0x30, 0x30, 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
	0x72, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2d, 0x74, 0x72, 0x61, 0x79, 0x00, 0x86, 0x74, 0x79,
	0x70, 0x65, 0x3d, 0x73, 0x68, 0x65, 0x65, 0x74, 0x46, 0x65, 0x65, 0x64, 0x41, 0x75, 0x74, 0x6f,
	0x4e, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x3b, 0x64, 0x69, 0x6d,
	0x75, 0x6e, 0x69, 0x74, 0x3d, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
	0x3b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x66, 0x65, 0x65, 0x64, 0x3d, 0x32, 0x39, 0x37, 0x30, 0x30,
	0x30, 0x3b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x78, 0x66, 0x65, 0x65, 0x64, 0x3d, 0x32, 0x31, 0x30,
	0x30, 0x30, 0x30, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3d,
	0x2d, 0x32, 0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x2d, 0x32, 0x3b, 0x73, 0x74, 0x61, 0x74,
	0x75, 0x73, 0x3d, 0x30, 0x3b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x54,
	0x72, 0x61, 0x79, 0x31, 0x30, 0x00, 0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6f,
	0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x74, 0x72, 0x61, 0x79, 0x00, 0x6e, 0x74, 0x79, 0x70, 0x65,
	0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61,
	0x63, 0x69, 0x74, 0x79, 0x3d, 0x2d, 0x32, 0x3b, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e,
	0x67, 0x3d, 0x2d, 0x32, 0x3b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x35, 0x3b, 0x73, 0x74,
	0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3d, 0x75, 0x6e, 0x6b, 0x6e,
	0x6f, 0x77, 0x6e, 0x3b, 0x70, 0x61, 0x67, 0x65, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79,
	0x3d, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x3b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x4f,
	0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x72, 0x61, 0x79, 0x31, 0x21, 0x00, 0x0e, 0x63, 0x6f, 0x70,
	0x69, 0x65, 0x73, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00,
	0x01, 0x23, 0x00, 0x12, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x64,
	0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x13, 0x00, 0x16, 0x66,
	0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x64, 0x65,
	0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x23, 0x00, 0x1d, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74,
	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d,
	0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x15,
	0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x64, 0x65,
	0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x32, 0x00, 0x1a, 0x70, 0x72,
	0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
	0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00,
	0x02, 0x58, 0x03, 0x44, 0x00, 0x0d, 0x73, 0x69, 0x64, 0x65, 0x73, 0x2d, 0x64, 0x65, 0x66, 0x61,
	0x75, 0x6c, 0x74, 0x00, 0x09, 0x6f, 0x6e, 0x65, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x44, 0x00,
	0x12, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x62, 0x69, 0x6e, 0x2d, 0x64, 0x65, 0x66, 0x61,
	0x75, 0x6c, 0x74, 0x00, 0x09, 0x66, 0x61, 0x63, 0x65, 0x2d, 0x64, 0x6f, 0x77, 0x6e, 0x44, 0x00,
	0x13, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x64, 0x65, 0x66,
	0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x18, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x64, 0x65,
	0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x22, 0x6d, 0x75,
	0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
	0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
	0x00, 0x24, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d,
	0x65, 0x6e, 0x74, 0x73, 0x2d, 0x75, 0x6e, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x2d,
	0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x21, 0x00, 0x11, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d,
	0x75, 0x70, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
	0x44, 0x00, 0x28, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
	0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
	0x2d, 0x75, 0x70, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x10, 0x74, 0x6f, 0x72,
	0x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x6f, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x44, 0x00, 0x1e,
	0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2d,
	0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04,
	0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x61,
	0x6c, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75,
	0x74, 0x6f, 0x33, 0x00, 0x10, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70,
	0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x23,
	0x00, 0x14, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x73, 0x75, 0x70,
	0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x33, 0x00, 0x1b, 0x6a,
	0x6f, 0x62, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x70, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x74,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01,
	0x7f, 0xff, 0xff, 0xff, 0x23, 0x00, 0x1f, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69,
	0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x73, 0x75, 0x70,
	0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00,
	0x04, 0x00, 0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x23, 0x00,
	0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07,
	0x23, 0x00, 0x17, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03,
	0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
	0x00, 0x05, 0x32, 0x00, 0x1c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x72, 0x65, 0x73,
	0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x09, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x32, 0x00, 0x00, 0x00,
	0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58, 0x03, 0x32, 0x00, 0x00, 0x00, 0x09, 0x00,
	0x00, 0x04, 0xb0, 0x00, 0x00, 0x04, 0xb0, 0x03, 0x44, 0x00, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x73,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x6f, 0x6e, 0x65, 0x2d,
	0x73, 0x69, 0x64, 0x65, 0x64, 0x44, 0x00, 0x00, 0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69,
	0x64, 0x65, 0x64, 0x2d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00,
	0x00, 0x00, 0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d, 0x6c, 0x6f, 0x6e,
	0x67, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d,
	0x62, 0x69, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x66,
	0x61, 0x63, 0x65, 0x2d, 0x64, 0x6f, 0x77, 0x6e, 0x44, 0x00, 0x15, 0x6f, 0x75, 0x74, 0x70, 0x75,
	0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x2d,
	0x6d, 0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x6d,
	0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x05, 0x63, 0x6f,
	0x6c, 0x6f, 0x72, 0x44, 0x00, 0x1a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6c, 0x6f,
	0x72, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
	0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x2d,
	0x6d, 0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x6d,
	0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x05, 0x63, 0x6f,
	0x6c, 0x6f, 0x72, 0x44, 0x00, 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2d,
	0x6d, 0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x22, 0x00, 0x15, 0x70, 0x61, 0x67,
	0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
	0x65, 0x64, 0x00, 0x01, 0x01, 0x44, 0x00, 0x24, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
	0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69,
	0x6e, 0x67, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x24, 0x73, 0x65,
	0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,
	0x2d, 0x75, 0x6e, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x63, 0x6f, 0x70, 0x69,
	0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x22, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x2d,
	0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74,
	0x65, 0x64, 0x2d, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x21, 0x00, 0x13, 0x6e, 0x75, 0x6d, 0x62,
	0x65, 0x72, 0x2d, 0x75, 0x70, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00,
	0x04, 0x00, 0x00, 0x00, 0x01, 0x44, 0x00, 0x2a, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61,
	0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6e,
	0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d, 0x75, 0x70, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
	0x65, 0x64, 0x00, 0x10, 0x74, 0x6f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x6f, 0x62, 0x6f,
	0x74, 0x74, 0x6f, 0x6d, 0x44, 0x00, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x05, 0x70, 0x61, 0x67, 0x65,
	0x73, 0x44, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44, 0x00, 0x00, 0x00, 0x09,
	0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x44, 0x00, 0x20, 0x70, 0x72, 0x69, 0x6e,
	0x74, 0x2d, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x69, 0x6e, 0x74, 0x65,
	0x6e, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x61, 0x75,
	0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61,
	0x6c, 0x44, 0x00, 0x17, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e,
	0x67, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x61, 0x75, 0x74,
	0x6f, 0x44, 0x00, 0x00, 0x00, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x2d, 0x66, 0x69, 0x74, 0x44, 0x00,
	0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x6c, 0x44, 0x00, 0x00, 0x00, 0x03, 0x66, 0x69, 0x74, 0x44,
	0x00, 0x00, 0x00, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x45, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x69, 0x63, 0x6f, 0x6e, 0x73, 0x00, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
	0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x77, 0x65, 0x62, 0x41, 0x70,
	0x70, 0x73, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
	0x72, 0x2d, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x2e, 0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00, 0x2b,
	0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
	0x2f, 0x77, 0x65, 0x62, 0x41, 0x70, 0x70, 0x73, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f,
	0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00, 0x31,
	0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
	0x2f, 0x77, 0x65, 0x62, 0x41, 0x70, 0x70, 0x73, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f,
	0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x2e, 0x70, 0x6e,
	0x67, 0x45, 0x00, 0x17, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x75, 0x70, 0x70,
	0x6c, 0x79, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x75, 0x72, 0x69, 0x00, 0x26, 0x68, 0x74, 0x74,
	0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x23, 0x68,
	0x49, 0x64, 0x2d, 0x70, 0x67, 0x49, 0x6e, 0x6b, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x61, 0x62,
	0x6c, 0x65, 0x73, 0x44, 0x00, 0x1e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74,
	0x65, 0x6e, 0x74, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x2d, 0x64, 0x65, 0x66,
	0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x20, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d,
	0x69, 0x7a, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x61,
	0x75, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x44, 0x00, 0x00,
	0x00, 0x08, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x44, 0x00, 0x00, 0x00, 0x04, 0x74,
	0x65, 0x78, 0x74, 0x44, 0x00, 0x00, 0x00, 0x11, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6e, 0x64,
	0x2d, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x44, 0x00, 0x1e, 0x70, 0x77, 0x67, 0x2d,
	0x72, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
	0x73, 0x68, 0x65, 0x65, 0x74, 0x2d, 0x62, 0x61, 0x63, 0x6b, 0x00, 0x07, 0x72, 0x6f, 0x74, 0x61,
	0x74, 0x65, 0x64, 0x44, 0x00, 0x22, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73, 0x74, 0x65, 0x72,
	0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x2d, 0x73,
	0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x07, 0x73, 0x67, 0x72, 0x61, 0x79, 0x5f,
	0x38, 0x44, 0x00, 0x00, 0x00, 0x06, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x38, 0x44, 0x00, 0x00, 0x00,
	0x0b, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x72, 0x67, 0x62, 0x5f, 0x38, 0x44, 0x00, 0x00, 0x00,
	0x05, 0x72, 0x67, 0x62, 0x5f, 0x38, 0x32, 0x00, 0x28, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73,
	0x74, 0x65, 0x72, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x73,
	0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x09, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x32, 0x00, 0x00, 0x00,
	0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58, 0x03, 0x41, 0x00, 0x16, 0x65, 0x70, 0x63,
	0x6c, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
	0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2e, 0x30, 0x22, 0x00, 0x17, 0x6d, 0x61, 0x6e, 0x75, 0x61,
	0x6c, 0x2d, 0x64, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
	0x65, 0x64, 0x00, 0x01, 0x00, 0x32, 0x00, 0x20, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x6f, 0x75,
	0x72, 0x63, 0x65, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73,
	0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00,
	0x01, 0x2c, 0x03, 0x32, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58,
	0x03, 0x32, 0x00, 0x1e, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d,
	0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75,
	0x6c, 0x74, 0x00, 0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58, 0x03, 0x21, 0x00, 0x1b,
	0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x70, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68,
	0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00,
	0x20, 0x21, 0x00, 0x1b, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x70, 0x2d, 0x68,
	0x65, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x00,
	0x04, 0x00, 0x00, 0x00, 0x20, 0x44, 0x00, 0x15, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x72, 0x61, 0x73,
	0x74, 0x65, 0x72, 0x2d, 0x62, 0x61, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x00, 0x07, 0x72,
	0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x00, 0x21, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x63, 0x6f,
	0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
	0x2d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x00, 0x04, 0x6a, 0x70, 0x65, 0x67,
	0x44, 0x00, 0x00, 0x00, 0x05, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x03, 0x72,
	0x6c, 0x65, 0x44, 0x00, 0x22, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f,
	0x72, 0x6d, 0x61, 0x74, 0x2d, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x2d, 0x61, 0x74, 0x74,
	0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x06, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x44,
	0x00, 0x00, 0x00, 0x15, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x44, 0x00, 0x20, 0x70, 0x72, 0x69, 0x6e,
	0x74, 0x65, 0x72, 0x2d, 0x67, 0x65, 0x74, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
	0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x0f, 0x64, 0x6f,
	0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x41, 0x00, 0x14,
	0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61,
	0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x41, 0x00, 0x1b, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
	0x2d, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2d,
	0x75, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x44, 0x00, 0x18, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
	0x79, 0x2d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
	0x74, 0x00, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x00, 0x1a, 0x69, 0x64, 0x65,
	0x6e, 0x74, 0x69, 0x66, 0x79, 0x2d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75,
	0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
	0x44, 0x00, 0x00, 0x00, 0x05, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x00, 0x00, 0x00, 0x03, 0x70,
	0x69, 0x6e, 0x23, 0x00, 0x1a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61,
	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00,
	0x04, 0x00, 0x00, 0x00, 0x0a, 0x21, 0x00, 0x1b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
	0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x2d,
	0x6f, 0x75, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78, 0x22, 0x00, 0x20, 0x6d, 0x75, 0x6c, 0x74,
	0x69, 0x70, 0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x6a, 0x6f,
	0x62, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x00, 0x03,
}

// Get-Printer-Attributes output from Pantum M7300FDW
var attrsPantumM7300FDW = []byte{
	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x47, 0x00, 0x12,
	0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x63,
	0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2d,
	0x38, 0x48, 0x00, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
	0x65, 0x73, 0x2d, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x2d, 0x6c,
	0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x05, 0x65, 0x6e, 0x2d,
	0x55, 0x53, 0x04, 0x22, 0x00, 0x0f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d,
	0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x00,
	0x49, 0x00, 0x19, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
	0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
	0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
	0x75, 0x72, 0x66, 0x49, 0x00, 0x00, 0x00, 0x10, 0x69, 0x6d, 0x61, 0x67,
	0x65, 0x2f, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73, 0x74, 0x65, 0x72,
	0x49, 0x00, 0x00, 0x00, 0x18, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
	0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d, 0x73,
	0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x00, 0x10, 0x6d, 0x6f, 0x70, 0x72,
	0x69, 0x61, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64,
	0x00, 0x03, 0x31, 0x2e, 0x33, 0x41, 0x00, 0x10, 0x70, 0x72, 0x69, 0x6e,
	0x74, 0x65, 0x72, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
	0x00, 0x0e, 0x70, 0x61, 0x6e, 0x74, 0x75, 0x6d, 0x20, 0x70, 0x72, 0x69,
	0x6e, 0x74, 0x65, 0x72, 0x41, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x12, 0x50, 0x61, 0x6e,
	0x74, 0x75, 0x6d, 0x20, 0x49, 0x50, 0x50, 0x20, 0x70, 0x72, 0x69, 0x6e,
	0x74, 0x65, 0x72, 0x45, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
	0x72, 0x2d, 0x6d, 0x6f, 0x72, 0x65, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x00,
	0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61,
	0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x36, 0x30, 0x30, 0x30, 0x30, 0x2f,
	0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x6a, 0x75, 0x6d, 0x70, 0x2e, 0x68,
	0x74, 0x6d, 0x6c, 0x41, 0x00, 0x16, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
	0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d,
	0x6f, 0x64, 0x65, 0x6c, 0x00, 0x16, 0x50, 0x61, 0x6e, 0x74, 0x75, 0x6d,
	0x20, 0x4d, 0x37, 0x33, 0x30, 0x30, 0x46, 0x44, 0x57, 0x20, 0x53, 0x65,
	0x72, 0x69, 0x65, 0x73, 0x41, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74,
	0x65, 0x72, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x69, 0x64,
	0x00, 0x45, 0x4d, 0x46, 0x47, 0x3a, 0x50, 0x61, 0x6e, 0x74, 0x75, 0x6d,
	0x3b, 0x43, 0x4d, 0x44, 0x3a, 0x3a, 0x44, 0x57, 0x2d, 0x50, 0x53, 0x2c,
	0x44, 0x57, 0x2d, 0x50, 0x43, 0x4c, 0x2c, 0x55, 0x52, 0x46, 0x3b, 0x4d,
	0x44, 0x4c, 0x3a, 0x2d, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b,
	0x43, 0x4c, 0x53, 0x3a, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x3b,
	0x44, 0x45, 0x53, 0x3a, 0x50, 0x61, 0x6e, 0x74, 0x75, 0x6d, 0x3b, 0x45,
	0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x75,
	0x69, 0x64, 0x00, 0x2d, 0x75, 0x72, 0x6e, 0x3a, 0x75, 0x75, 0x69, 0x64,
	0x3a, 0x30, 0x37, 0x62, 0x63, 0x35, 0x61, 0x34, 0x37, 0x2d, 0x65, 0x64,
	0x66, 0x32, 0x2d, 0x34, 0x36, 0x36, 0x31, 0x2d, 0x38, 0x37, 0x34, 0x38,
	0x2d, 0x38, 0x36, 0x32, 0x30, 0x66, 0x37, 0x39, 0x64, 0x34, 0x61, 0x63,
	0x34, 0x45, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
	0x69, 0x63, 0x6f, 0x6e, 0x73, 0x00, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
	0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a,
	0x36, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x68,
	0x69, 0x70, 0x34, 0x38, 0x2e, 0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00,
	0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61,
	0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x36, 0x30, 0x30, 0x30, 0x30, 0x2f,
	0x66, 0x6c, 0x61, 0x67, 0x73, 0x68, 0x69, 0x70, 0x31, 0x32, 0x38, 0x2e,
	0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00, 0x26, 0x68, 0x74, 0x74, 0x70,
	0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
	0x3a, 0x36, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73,
	0x68, 0x69, 0x70, 0x35, 0x31, 0x32, 0x2e, 0x70, 0x6e, 0x67, 0x42, 0x00,
	0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x6e, 0x73,
	0x2d, 0x73, 0x64, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x1d, 0x50, 0x61,
	0x6e, 0x74, 0x75, 0x6d, 0x20, 0x4d, 0x37, 0x33, 0x30, 0x30, 0x46, 0x44,
	0x57, 0x20, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x31, 0x43, 0x36,
	0x46, 0x43, 0x44, 0x44, 0x00, 0x0d, 0x75, 0x72, 0x66, 0x2d, 0x73, 0x75,
	0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x56, 0x31, 0x2e,
	0x34, 0x44, 0x00, 0x00, 0x00, 0x02, 0x57, 0x38, 0x44, 0x00, 0x00, 0x00,
	0x03, 0x49, 0x53, 0x31, 0x44, 0x00, 0x00, 0x00, 0x04, 0x43, 0x50, 0x39,
	0x39, 0x44, 0x00, 0x00, 0x00, 0x03, 0x50, 0x51, 0x34, 0x44, 0x00, 0x00,
	0x00, 0x04, 0x4f, 0x42, 0x31, 0x30, 0x44, 0x00, 0x00, 0x00, 0x05, 0x52,
	0x53, 0x36, 0x30, 0x30, 0x44, 0x00, 0x00, 0x00, 0x03, 0x44, 0x4d, 0x31,
	0x44, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6b,
	0x69, 0x6e, 0x64, 0x00, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
	0x74, 0x44, 0x00, 0x00, 0x00, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
	0x70, 0x65, 0x34, 0x00, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73,
	0x69, 0x7a, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
	0x64, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x21,
	0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x00, 0x04, 0x00, 0x00, 0x6d, 0x24, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69,
	0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x52,
	0x08, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
	0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00,
	0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00,
	0x00, 0x39, 0xd0, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b,
	0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00,
	0x04, 0x00, 0x00, 0x29, 0x04, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69,
	0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x39,
	0xd0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x00, 0x04, 0x00, 0x00, 0x2c, 0x88, 0x21, 0x00, 0x0b, 0x79, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00,
	0x00, 0x3f, 0x48, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
	0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
	0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x2a, 0xf8, 0x21, 0x00, 0x0b,
	0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00,
	0x04, 0x00, 0x00, 0x55, 0xf0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00,
	0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x27, 0x10, 0x21,
	0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x37, 0x00, 0x00, 0x00, 0x00,
	0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69,
	0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x2e,
	0xe0, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
	0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x5b, 0xcc, 0x37, 0x00, 0x00,
	0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d,
	0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00,
	0x00, 0x28, 0xed, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65,
	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x5e, 0x42, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b,
	0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00,
	0x04, 0x00, 0x00, 0x54, 0x56, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69,
	0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x8a,
	0xe8, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x33,
	0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x00, 0x08, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x39, 0xd0, 0x33,
	0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
	0x6e, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00, 0x00, 0x8b, 0x10, 0x37,
	0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x73,
	0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09,
	0x6f, 0x6e, 0x65, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x44, 0x00, 0x00,
	0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d,
	0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00,
	0x00, 0x00, 0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64,
	0x2d, 0x6c, 0x6f, 0x6e, 0x67, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x03,
}
0707010000000B000081A400000000000000000000000167F56AFB00001300000000000000000000000000000000000000001700000000goipp-1.2.0/encoder.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Message encoder
 */

package goipp

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math"
)

// Type messageEncoder represents Message encoder
type messageEncoder struct {
	out io.Writer // Output stream
}

// Encode the message
func (me *messageEncoder) encode(m *Message) error {
	// Wire format:
	//
	//   2 bytes:  Version
	//   2 bytes:  Code (Operation or Status)
	//   4 bytes:  RequestID
	//   variable: attributes
	//   1 byte:   TagEnd

	// Encode message header
	var err error
	err = me.encodeU16(uint16(m.Version))
	if err == nil {
		err = me.encodeU16(uint16(m.Code))
	}
	if err == nil {
		err = me.encodeU32(uint32(m.RequestID))
	}

	// Encode attributes
	for _, grp := range m.AttrGroups() {
		err = me.encodeTag(grp.Tag)
		if err == nil {
			for _, attr := range grp.Attrs {
				if attr.Name == "" {
					err = errors.New("Attribute without name")
				} else {
					err = me.encodeAttr(attr, true)
				}
			}
		}

		if err != nil {
			break
		}
	}

	if err == nil {
		err = me.encodeTag(TagEnd)
	}

	return err
}

// Encode attribute
func (me *messageEncoder) encodeAttr(attr Attribute, checkTag bool) error {
	// Wire format
	//     1 byte:   Tag
	//     2 bytes:  len(Name)
	//     variable: name
	//     2 bytes:  len(Value)
	//     variable  Value
	//
	// And each additional value comes as attribute
	// without name
	if len(attr.Values) == 0 {
		return errors.New("Attribute without value")
	}

	name := attr.Name
	for _, val := range attr.Values {
		tag := val.T

		if checkTag {
			if tag.IsDelimiter() || tag == TagMemberName || tag == TagEndCollection {
				return fmt.Errorf("Tag %s cannot be used with value", tag)
			}

			if uint(tag) >= 0x100 {
				return fmt.Errorf("Tag %s out of range", tag)
			}
		}

		err := me.encodeTag(tag)
		if err != nil {
			return err
		}

		err = me.encodeName(name)
		if err != nil {
			return err
		}

		err = me.encodeValue(val.T, val.V)
		if err != nil {
			return err
		}

		name = "" // Each additional value comes without name
	}

	return nil
}

// Encode 8-bit integer
func (me *messageEncoder) encodeU8(v uint8) error {
	return me.write([]byte{v})
}

// Encode 16-bit integer
func (me *messageEncoder) encodeU16(v uint16) error {
	return me.write([]byte{byte(v >> 8), byte(v)})
}

// Encode 32-bit integer
func (me *messageEncoder) encodeU32(v uint32) error {
	return me.write([]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
}

// Encode Tag
func (me *messageEncoder) encodeTag(tag Tag) error {
	return me.encodeU8(byte(tag))
}

// Encode Attribute name
func (me *messageEncoder) encodeName(name string) error {
	if len(name) > math.MaxInt16 {
		return fmt.Errorf("Attribute name exceeds %d bytes",
			math.MaxInt16)
	}

	err := me.encodeU16(uint16(len(name)))
	if err == nil {
		err = me.write([]byte(name))
	}

	return err
}

// Encode Attribute value
func (me *messageEncoder) encodeValue(tag Tag, v Value) error {
	// Check Value type vs the Tag
	tagType := tag.Type()
	if tagType == TypeVoid {
		v = Void{} // Ignore supplied value
	} else if tagType != v.Type() {
		return fmt.Errorf("Tag %s: %s value required, %s present",
			tag, tagType, v.Type())
	}

	// Convert Value to bytes in wire representation.
	data, err := v.encode()
	if err != nil {
		return err
	}

	if len(data) > math.MaxInt16 {
		return fmt.Errorf("Attribute value exceeds %d bytes",
			math.MaxInt16)
	}

	// TagExtension encoding rules enforcement.
	if tag == TagExtension {
		if len(data) < 4 {
			return fmt.Errorf(
				"Extension tag truncated (%d bytes)", len(data))
		}

		t := binary.BigEndian.Uint32(data)
		if t > 0x7fffffff {
			return fmt.Errorf(
				"Extension tag 0x%8.8x out of range", t)
		}
	}

	// Encode the value
	err = me.encodeU16(uint16(len(data)))
	if err == nil {
		err = me.write(data)
	}

	// Handle collection
	if collection, ok := v.(Collection); ok {
		return me.encodeCollection(tag, collection)
	}

	return err
}

// Encode collection
func (me *messageEncoder) encodeCollection(tag Tag, collection Collection) error {
	for _, attr := range collection {
		if attr.Name == "" {
			return errors.New("Collection member without name")
		}

		attrName := MakeAttribute("", TagMemberName, String(attr.Name))

		err := me.encodeAttr(attrName, false)
		if err == nil {
			err = me.encodeAttr(
				Attribute{Name: "", Values: attr.Values}, true)
		}

		if err != nil {
			return err
		}
	}

	return me.encodeAttr(MakeAttribute("", TagEndCollection, Void{}), false)
}

// Write a piece of raw data to output stream
func (me *messageEncoder) write(data []byte) error {
	for len(data) > 0 {
		n, err := me.out.Write(data)
		if err != nil {
			return err
		}
		data = data[n:]
	}

	return nil
}
0707010000000C000081A400000000000000000000000167F56AFB00001402000000000000000000000000000000000000001900000000goipp-1.2.0/formatter.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP formatter (pretty-printer)
 */

package goipp

import (
	"bytes"
	"fmt"
	"io"
	"strings"
)

// Formatter parameters:
const (
	// FormatterIndentShift is the indentation shift, number of space
	// characters per indentation level.
	FormatterIndentShift = 4
)

// Formatter formats IPP messages, attributes, groups etc
// for pretty-printing.
//
// It supersedes [Message.Print] method which is now considered
// deprecated.
type Formatter struct {
	indent     int          // Indentation level
	userIndent int          // User-settable indent
	buf        bytes.Buffer // Output buffer
}

// NewFormatter returns a new Formatter.
func NewFormatter() *Formatter {
	return &Formatter{}
}

// Reset resets the formatter.
func (f *Formatter) Reset() {
	f.buf.Reset()
	f.indent = 0
}

// SetIndent configures indentation. If parameter is greater that
// zero, the specified amount of white space will prepended to each
// non-empty output line.
func (f *Formatter) SetIndent(n int) {
	f.userIndent = 0
	if n > 0 {
		f.userIndent = n
	}
}

// Bytes returns formatted text as a byte slice.
func (f *Formatter) Bytes() []byte {
	return f.buf.Bytes()
}

// String returns formatted text as a string.
func (f *Formatter) String() string {
	return f.buf.String()
}

// WriteTo writes formatted text to w.
// It implements [io.WriterTo] interface.
func (f *Formatter) WriteTo(w io.Writer) (int64, error) {
	return f.buf.WriteTo(w)
}

// Printf writes formatted line into the [Formatter], automatically
// indented and with added newline at the end.
//
// It returns the number of bytes written and nil as an error (for
// consistency with other printf-like functions).
func (f *Formatter) Printf(format string, args ...interface{}) (int, error) {
	s := fmt.Sprintf(format, args...)
	lines := strings.Split(s, "\n")
	cnt := 0

	for _, line := range lines {
		if line != "" {
			cnt += f.doIndent()
		}

		f.buf.WriteString(line)
		f.buf.WriteByte('\n')
		cnt += len(line) + 1
	}

	return cnt, nil
}

// FmtRequest formats a request [Message].
func (f *Formatter) FmtRequest(msg *Message) {
	f.fmtMessage(msg, true)
}

// FmtResponse formats a response [Message].
func (f *Formatter) FmtResponse(msg *Message) {
	f.fmtMessage(msg, false)
}

// fmtMessage formats a request or response Message.
func (f *Formatter) fmtMessage(msg *Message, request bool) {
	f.Printf("{")
	f.indent++

	f.Printf("REQUEST-ID %d", msg.RequestID)
	f.Printf("VERSION %s", msg.Version)

	if request {
		f.Printf("OPERATION %s", Op(msg.Code))
	} else {
		f.Printf("STATUS %s", Status(msg.Code))
	}

	if groups := msg.AttrGroups(); len(groups) != 0 {
		f.Printf("")
		f.FmtGroups(groups)
	}

	f.indent--
	f.Printf("}")
}

// FmtGroups formats a [Groups] slice.
func (f *Formatter) FmtGroups(groups Groups) {
	for i, g := range groups {
		if i != 0 {
			f.Printf("")
		}
		f.FmtGroup(g)
	}
}

// FmtGroup formats a single [Group].
func (f *Formatter) FmtGroup(g Group) {
	f.Printf("GROUP %s", g.Tag)
	f.FmtAttributes(g.Attrs)
}

// FmtAttributes formats a [Attributes] slice.
func (f *Formatter) FmtAttributes(attrs Attributes) {
	for _, attr := range attrs {
		f.FmtAttribute(attr)
	}
}

// FmtAttribute formats a single [Attribute].
func (f *Formatter) FmtAttribute(attr Attribute) {
	f.fmtAttributeOrMember(attr, false)
}

// FmtAttributes formats a single [Attribute] or collection member.
func (f *Formatter) fmtAttributeOrMember(attr Attribute, member bool) {
	buf := &f.buf

	f.doIndent()
	if member {
		fmt.Fprintf(buf, "MEMBER %q", attr.Name)
	} else {
		fmt.Fprintf(buf, "ATTR %q", attr.Name)
	}

	tag := TagZero
	for _, val := range attr.Values {
		if val.T != tag {
			fmt.Fprintf(buf, " %s:", val.T)
			tag = val.T
		}

		if collection, ok := val.V.(Collection); ok {
			if f.onNL() {
				f.Printf("{")
			} else {
				buf.Write([]byte(" {\n"))
			}

			f.indent++

			for _, attr2 := range collection {
				f.fmtAttributeOrMember(attr2, true)
			}

			f.indent--
			f.Printf("}")
		} else {
			fmt.Fprintf(buf, " %s", val.V)
		}
	}

	f.forceNL()
}

// onNL returns true if Formatter is at the beginning of new line.
func (f *Formatter) onNL() bool {
	b := f.buf.Bytes()
	return len(b) == 0 || b[len(b)-1] == '\n'
}

// forceNL inserts newline character if Formatter is not at the.
// beginning of new line
func (f *Formatter) forceNL() {
	if !f.onNL() {
		f.buf.WriteByte('\n')
	}
}

// doIndent outputs indentation space.
// It returns number of characters written.
func (f *Formatter) doIndent() int {
	cnt := FormatterIndentShift * f.indent
	cnt += f.userIndent

	n := cnt
	for n > len(formatterSomeSpace) {
		f.buf.Write([]byte(formatterSomeSpace[:]))
		n -= len(formatterSomeSpace)
	}

	f.buf.Write([]byte(formatterSomeSpace[:n]))

	return cnt
}

// formatterSomeSpace contains some space characters for
// fast output of indentation space.
var formatterSomeSpace [64]byte

func init() {
	for i := range formatterSomeSpace {
		formatterSomeSpace[i] = ' '
	}
}
0707010000000D000081A400000000000000000000000167F56AFB00001B0C000000000000000000000000000000000000001E00000000goipp-1.2.0/formatter_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP formatter test
 */

package goipp

import (
	"strings"
	"testing"
)

// TestFmtAttribute runs Formatter.FmtAttribute tests
func TestFmtAttribute(t *testing.T) {
	type testData struct {
		attr   Attribute // Inpur attribute
		out    []string  // Expected output
		indent int       // Indentation
	}

	tests := []testData{
		// Simple test
		{
			attr: MakeAttr(
				"attributes-charset",
				TagCharset,
				String("utf-8")),
			out: []string{
				`ATTR "attributes-charset" charset: utf-8`,
			},
		},

		// Simple test with indentation
		{
			attr: MakeAttr(
				"attributes-charset",
				TagCharset,
				String("utf-8")),
			indent: 2,
			out: []string{
				`  ATTR "attributes-charset" charset: utf-8`,
			},
		},

		// Simple test with huge indentation
		{
			attr: MakeAttr(
				"attributes-charset",
				TagCharset,
				String("utf-8")),
			indent: 123,
			out: []string{
				strings.Repeat(" ", 123) +
					`ATTR "attributes-charset" charset: utf-8`,
			},
		},

		// Collection
		{
			attr: MakeAttrCollection("media-col",
				MakeAttrCollection("media-size",
					MakeAttribute("x-dimension",
						TagInteger, Integer(10160)),
					MakeAttribute("y-dimension",
						TagInteger, Integer(15240)),
				),
				MakeAttribute("media-left-margin",
					TagInteger, Integer(0)),
				MakeAttribute("media-right-margin",
					TagInteger, Integer(0)),
				MakeAttribute("media-top-margin",
					TagInteger, Integer(0)),
				MakeAttribute("media-bottom-margin",
					TagInteger, Integer(0)),
			),

			out: []string{
				`ATTR "media-col" collection: {`,
				`    MEMBER "media-size" collection: {`,
				`        MEMBER "x-dimension" integer: 10160`,
				`        MEMBER "y-dimension" integer: 15240`,
				`    }`,
				`    MEMBER "media-left-margin" integer: 0`,
				`    MEMBER "media-right-margin" integer: 0`,
				`    MEMBER "media-top-margin" integer: 0`,
				`    MEMBER "media-bottom-margin" integer: 0`,
				`}`,
			},
		},

		// 1SetOf Collection
		{
			attr: MakeAttr("media-size-supported",
				TagBeginCollection,
				Collection{
					MakeAttribute("x-dimension",
						TagInteger, Integer(20990)),
					MakeAttribute("y-dimension",
						TagInteger, Integer(29704)),
				},
				Collection{
					MakeAttribute("x-dimension",
						TagInteger, Integer(14852)),
					MakeAttribute("y-dimension",
						TagInteger, Integer(20990)),
				},
			),
			indent: 2,
			out: []string{
				`  ATTR "media-size-supported" collection: {`,
				`      MEMBER "x-dimension" integer: 20990`,
				`      MEMBER "y-dimension" integer: 29704`,
				`  }`,
				`  {`,
				`      MEMBER "x-dimension" integer: 14852`,
				`      MEMBER "y-dimension" integer: 20990`,
				`  }`,
			},
		},

		// Multiple values
		{
			attr: MakeAttr("page-delivery-supported",
				TagKeyword,
				String("reverse-order"),
				String("same-order")),

			out: []string{
				`ATTR "page-delivery-supported" keyword: reverse-order same-order`,
			},
		},

		// Values of mixed type
		{
			attr: Attribute{
				Name: "page-ranges",
				Values: Values{
					{TagInteger, Integer(1)},
					{TagInteger, Integer(2)},
					{TagInteger, Integer(3)},
					{TagRange, Range{5, 7}},
				},
			},

			out: []string{
				`ATTR "page-ranges" integer: 1 2 3 rangeOfInteger: 5-7`,
			},
		},
	}

	f := NewFormatter()
	for _, test := range tests {
		f.Reset()
		f.SetIndent(test.indent)

		expected := strings.Join(test.out, "\n") + "\n"
		f.FmtAttribute(test.attr)
		out := f.String()

		if out != expected {
			t.Errorf("output mismatch\n"+
				"expected:\n%s"+
				"present:\n%s",
				expected, out)
		}
	}
}

// TestFmtRequestResponse runs Formatter.FmtRequest and
// Formatter.FmtResponse tests
func TestFmtRequestResponse(t *testing.T) {
	type testData struct {
		msg *Message // Input message
		rq  bool     // This is request
		out []string // Expected output
	}

	tests := []testData{
		{
			msg: &Message{
				Version:   MakeVersion(2, 0),
				Code:      Code(OpGetPrinterAttributes),
				RequestID: 1,

				Operation: []Attribute{
					MakeAttribute(
						"attributes-charset",
						TagCharset,
						String("utf-8")),
					MakeAttribute(
						"attributes-natural-language",
						TagLanguage,
						String("en-us")),
					MakeAttribute(
						"requested-attributes",
						TagKeyword,
						String("printer-name")),
				},
			},
			rq: true,
			out: []string{
				`{`,
				`    REQUEST-ID 1`,
				`    VERSION 2.0`,
				`    OPERATION Get-Printer-Attributes`,
				``,
				`    GROUP operation-attributes-tag`,
				`    ATTR "attributes-charset" charset: utf-8`,
				`    ATTR "attributes-natural-language" naturalLanguage: en-us`,
				`    ATTR "requested-attributes" keyword: printer-name`,
				`}`,
			},
		},

		{
			msg: &Message{
				Version:   MakeVersion(2, 0),
				Code:      Code(StatusOk),
				RequestID: 1,

				Operation: []Attribute{
					MakeAttribute(
						"attributes-charset",
						TagCharset,
						String("utf-8")),
					MakeAttribute(
						"attributes-natural-language",
						TagLanguage,
						String("en-us")),
				},

				Printer: []Attribute{
					MakeAttribute(
						"printer-name",
						TagName,
						String("Kyocera_ECOSYS_M2040dn")),
				},
			},
			rq: false,
			out: []string{
				`{`,
				`    REQUEST-ID 1`,
				`    VERSION 2.0`,
				`    STATUS successful-ok`,
				``,
				`    GROUP operation-attributes-tag`,
				`    ATTR "attributes-charset" charset: utf-8`,
				`    ATTR "attributes-natural-language" naturalLanguage: en-us`,
				``,
				`    GROUP printer-attributes-tag`,
				`    ATTR "printer-name" nameWithoutLanguage: Kyocera_ECOSYS_M2040dn`,
				`}`,
			},
		},
	}

	f := NewFormatter()
	for _, test := range tests {
		f.Reset()
		if test.rq {
			f.FmtRequest(test.msg)
		} else {
			f.FmtResponse(test.msg)
		}

		out := f.String()
		expected := strings.Join(test.out, "\n") + "\n"

		if out != expected {
			t.Errorf("output mismatch\n"+
				"expected:\n%s"+
				"present:\n%s",
				expected, out)
		}
	}
}

// TestFmtBytes tests Formatter.Bytes function
func TestFmtBytes(t *testing.T) {
	msg := &Message{
		Version:   MakeVersion(2, 0),
		Code:      Code(OpGetPrinterAttributes),
		RequestID: 1,

		Operation: []Attribute{
			MakeAttribute(
				"attributes-charset",
				TagCharset,
				String("utf-8")),
			MakeAttribute(
				"attributes-natural-language",
				TagLanguage,
				String("en-us")),
			MakeAttribute(
				"requested-attributes",
				TagKeyword,
				String("printer-name")),
		},
	}

	f := NewFormatter()
	f.FmtRequest(msg)

	s := f.String()
	b := f.Bytes()

	// Note, we've already tested f.String() function in the previous
	// tests, so f.Bytes() is tested against it
	if string(b) != s {
		t.Errorf("Formatter.Bytes test failed:\n"+
			"expected: %s\n"+
			"present:  %s\n",
			s,
			b,
		)
	}
}
0707010000000E000081A400000000000000000000000167F56AFB0000002E000000000000000000000000000000000000001300000000goipp-1.2.0/go.modmodule github.com/OpenPrinting/goipp

go 1.11
0707010000000F000081A400000000000000000000000167F56AFB00000CE0000000000000000000000000000000000000001500000000goipp-1.2.0/group.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Groups of attributes
 */

package goipp

import "sort"

// Group represents a group of attributes.
//
// Since 1.1.0
type Group struct {
	Tag   Tag        // Group tag
	Attrs Attributes // Group attributes
}

// Groups represents a sequence of groups
//
// The primary purpose of this type is to represent
// messages with repeated groups with the same group tag
//
// # See Message type documentation for more details
//
// Since 1.1.0
type Groups []Group

// Add Attribute to the Group
func (g *Group) Add(attr Attribute) {
	g.Attrs.Add(attr)
}

// Equal checks that groups g and g2 are equal
func (g Group) Equal(g2 Group) bool {
	return g.Tag == g2.Tag && g.Attrs.Equal(g2.Attrs)
}

// Similar checks that groups g and g2 are **logically** equal.
func (g Group) Similar(g2 Group) bool {
	return g.Tag == g2.Tag && g.Attrs.Similar(g2.Attrs)
}

// Clone creates a shallow copy of the Group
func (g Group) Clone() Group {
	g2 := g
	g2.Attrs = g.Attrs.Clone()
	return g2
}

// DeepCopy creates a deep copy of the Group
func (g Group) DeepCopy() Group {
	g2 := g
	g2.Attrs = g.Attrs.DeepCopy()
	return g2
}

// Add Group to Groups
func (groups *Groups) Add(g Group) {
	*groups = append(*groups, g)
}

// Clone creates a shallow copy of Groups.
// For nil input it returns nil output.
func (groups Groups) Clone() Groups {
	var groups2 Groups
	if groups != nil {
		groups2 = make(Groups, len(groups))
		copy(groups2, groups)
	}
	return groups2
}

// DeepCopy creates a deep copy of Groups.
// For nil input it returns nil output.
func (groups Groups) DeepCopy() Groups {
	var groups2 Groups
	if groups != nil {
		groups2 = make(Groups, len(groups))
		for i := range groups {
			groups2[i] = groups[i].DeepCopy()
		}
	}
	return groups2
}

// Equal checks that groups and groups2 are equal.
//
// Note, Groups(nil) and Groups{} are not equal but similar.
func (groups Groups) Equal(groups2 Groups) bool {
	if len(groups) != len(groups2) {
		return false
	}

	if (groups == nil) != (groups2 == nil) {
		return false
	}

	for i, g := range groups {
		g2 := groups2[i]
		if !g.Equal(g2) {
			return false
		}
	}

	return true
}

// Similar checks that groups and groups2 are **logically** equal,
// which means the following:
//   - groups and groups2 contain the same set of
//     groups, but groups with different tags may
//     be reordered between each other.
//   - groups with the same tag cannot be reordered.
//   - attributes of corresponding groups are similar.
//
// Note, Groups(nil) and Groups{} are not equal but similar.
func (groups Groups) Similar(groups2 Groups) bool {
	// Fast check: if lengths are not the same, groups
	// are definitely not equal
	if len(groups) != len(groups2) {
		return false
	}

	// Sort groups by tag
	groups = groups.Clone()
	groups2 = groups2.Clone()

	sort.SliceStable(groups, func(i, j int) bool {
		return groups[i].Tag < groups[j].Tag
	})

	sort.SliceStable(groups2, func(i, j int) bool {
		return groups2[i].Tag < groups2[j].Tag
	})

	// Now compare, group by group
	for i := range groups {
		if !groups[i].Similar(groups2[i]) {
			return false
		}
	}

	return true
}
07070100000010000081A400000000000000000000000167F56AFB00001BA5000000000000000000000000000000000000001A00000000goipp-1.2.0/group_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Tests fop groups of attributes
 */

package goipp

import "testing"

// TestGroupEqualSimilar tests Group.Equal and Group.Similar
func TestGroupEqualSimilar(t *testing.T) {
	type testData struct {
		g1, g2  Group // A pair of Attributes slice
		equal   bool  // Expected g1.Equal(g2) output
		similar bool  // Expected g2.Similar(g2) output
	}

	attrs1 := Attributes{
		MakeAttr("attr1", TagInteger, Integer(1)),
		MakeAttr("attr2", TagInteger, Integer(2)),
		MakeAttr("attr3", TagInteger, Integer(3)),
	}

	attrs2 := Attributes{
		MakeAttr("attr3", TagInteger, Integer(3)),
		MakeAttr("attr2", TagInteger, Integer(2)),
		MakeAttr("attr1", TagInteger, Integer(1)),
	}

	tests := []testData{
		{
			g1:      Group{TagJobGroup, nil},
			g2:      Group{TagJobGroup, nil},
			equal:   true,
			similar: true,
		},

		{
			g1:      Group{TagJobGroup, Attributes{}},
			g2:      Group{TagJobGroup, Attributes{}},
			equal:   true,
			similar: true,
		},

		{
			g1:      Group{TagJobGroup, Attributes{}},
			g2:      Group{TagJobGroup, nil},
			equal:   false,
			similar: true,
		},

		{
			g1:      Group{TagJobGroup, attrs1},
			g2:      Group{TagJobGroup, attrs1},
			equal:   true,
			similar: true,
		},

		{
			g1:      Group{TagJobGroup, attrs1},
			g2:      Group{TagJobGroup, attrs2},
			equal:   false,
			similar: true,
		},
	}

	for _, test := range tests {
		equal := test.g1.Equal(test.g2)
		similar := test.g1.Similar(test.g2)

		if equal != test.equal {
			t.Errorf("testing Group.Equal:\n"+
				"attrs 1:   %s\n"+
				"attrs 2:   %s\n"+
				"expected:  %v\n"+
				"present:   %v\n",
				test.g1, test.g2,
				test.equal, equal,
			)
		}

		if similar != test.similar {
			t.Errorf("testing Group.Similar:\n"+
				"attrs 1:  %s\n"+
				"attrs 2:  %s\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.g1, test.g2,
				test.similar, similar,
			)
		}
	}
}

// TestGroupAdd tests Group.Add
func TestGroupAdd(t *testing.T) {
	g1 := Group{
		TagJobGroup,
		Attributes{
			MakeAttr("attr1", TagInteger, Integer(1)),
			MakeAttr("attr2", TagInteger, Integer(2)),
			MakeAttr("attr3", TagInteger, Integer(3)),
		},
	}

	g2 := Group{Tag: TagJobGroup}
	for _, attr := range g1.Attrs {
		g2.Add(attr)
	}

	if !g1.Equal(g2) {
		t.Errorf("Group.Add test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			g1, g2,
		)
	}
}

// TestGroupCopy tests Group.Clone and Group.DeepCopy
func TestGroupCopy(t *testing.T) {
	type testData struct {
		g Group
	}

	attrs := Attributes{
		MakeAttr("attr1", TagInteger, Integer(1)),
		MakeAttr("attr2", TagInteger, Integer(2)),
		MakeAttr("attr3", TagInteger, Integer(3)),
	}

	tests := []testData{
		{Group{TagJobGroup, nil}},
		{Group{TagJobGroup, Attributes{}}},
		{Group{TagJobGroup, attrs}},
	}

	for _, test := range tests {
		clone := test.g.Clone()

		if !test.g.Equal(clone) {
			t.Errorf("testing Group.Clone\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.g,
				clone,
			)
		}

		copy := test.g.DeepCopy()
		if !test.g.Equal(copy) {
			t.Errorf("testing Group.DeepCopy\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.g,
				copy,
			)
		}
	}
}

// TestGroupEqualSimilar tests Group.Equal and Group.Similar
func TestGroupsEqualSimilar(t *testing.T) {
	type testData struct {
		groups1, groups2 Groups // A pair of Attributes slice
		equal            bool   // Expected g1.Equal(g2) output
		similar          bool   // Expected g2.Similar(g2) output
	}

	g1 := Group{
		TagJobGroup,
		Attributes{MakeAttr("attr1", TagInteger, Integer(1))},
	}

	g2 := Group{
		TagJobGroup,
		Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
	}

	g3 := Group{
		TagPrinterGroup,
		Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
	}

	tests := []testData{
		{
			// nil equal and similar to nil
			groups1: nil,
			groups2: nil,
			equal:   true,
			similar: true,
		},

		{
			// Empty groups equal and similar to empty groups
			groups1: Groups{},
			groups2: Groups{},
			equal:   true,
			similar: true,
		},

		{
			// nil similar but not equal to empty groups
			groups1: nil,
			groups2: Groups{},
			equal:   false,
			similar: true,
		},

		{
			// groups of different size neither equal nor similar
			groups1: Groups{g1, g2, g3},
			groups2: Groups{g1, g2},
			equal:   false,
			similar: false,
		},

		{
			// Same list of groups: equal and similar
			groups1: Groups{g1, g2, g3},
			groups2: Groups{g1, g2, g3},
			equal:   true,
			similar: true,
		},

		{
			// Groups with different group tags reordered.
			// Similar but not equal.
			groups1: Groups{g1, g2, g3},
			groups2: Groups{g3, g1, g2},
			equal:   false,
			similar: true,
		},

		{
			// Groups with the same group tags reordered.
			// Neither equal nor similar
			groups1: Groups{g1, g2, g3},
			groups2: Groups{g2, g1, g3},
			equal:   false,
			similar: false,
		},
	}

	for _, test := range tests {
		equal := test.groups1.Equal(test.groups2)
		similar := test.groups1.Similar(test.groups2)

		if equal != test.equal {
			t.Errorf("testing Groups.Equal:\n"+
				"attrs 1:   %s\n"+
				"attrs 2:   %s\n"+
				"expected:  %v\n"+
				"present:   %v\n",
				test.groups1, test.groups2,
				test.equal, equal,
			)
		}

		if similar != test.similar {
			t.Errorf("testing Groups.Similar:\n"+
				"attrs 1:  %s\n"+
				"attrs 2:  %s\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.groups1, test.groups2,
				test.similar, similar,
			)
		}
	}
}

// TestGroupsAdd tests Groups.Add
func TestGroupsAdd(t *testing.T) {
	g1 := Group{
		TagJobGroup,
		Attributes{MakeAttr("attr1", TagInteger, Integer(1))},
	}

	g2 := Group{
		TagJobGroup,
		Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
	}

	g3 := Group{
		TagPrinterGroup,
		Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
	}

	groups1 := Groups{g1, g2, g3}

	groups2 := Groups{}
	groups2.Add(g1)
	groups2.Add(g2)
	groups2.Add(g3)

	if !groups1.Equal(groups2) {
		t.Errorf("Groups.Add test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			groups1, groups2,
		)
	}
}

// TestGroupsCopy tests Groups.Clone and Groups.DeepCopy
func TestGroupsCopy(t *testing.T) {
	g1 := Group{
		TagJobGroup,
		Attributes{MakeAttr("attr1", TagInteger, Integer(1))},
	}

	g2 := Group{
		TagJobGroup,
		Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
	}

	g3 := Group{
		TagPrinterGroup,
		Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
	}

	type testData struct {
		groups Groups
	}

	tests := []testData{
		{nil},
		{Groups{}},
		{Groups{g1, g2, g3}},
	}

	for _, test := range tests {
		clone := test.groups.Clone()

		if !test.groups.Equal(clone) {
			t.Errorf("testing Groups.Clone\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.groups,
				clone,
			)
		}

		copy := test.groups.DeepCopy()
		if !test.groups.Equal(copy) {
			t.Errorf("testing Groups.DeepCopy\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.groups,
				copy,
			)
		}
	}
}
07070100000011000081A400000000000000000000000167F56AFB00000352000000000000000000000000000000000000001500000000goipp-1.2.0/index.md# goipp

[![godoc.org](https://godoc.org/github.com/OpenPrinting/goipp?status.svg)](http://godoc.org/github.com/OpenPrinting/goipp)
![GitHub](https://img.shields.io/github/license/OpenPrinting/goipp)

The goipp library is fairly complete implementation of IPP core protocol in
pure Go. Essentially, it is  IPP messages parser/composer. Transport is
not implemented here, because Go standard library has an excellent built-in
HTTP client, and it doesn't make a lot of sense to wrap it here.

High-level requests, like "print a file" are also not implemented, only the
low-level stuff.

All documentation is on godoc.org -- follow the link above. Pull requests
are welcomed, assuming they don't break existing API.

For more information and software downloads, please visit the
[Project's page at GitHub](https://github.com/OpenPrinting/sane-airscan)

07070100000012000081A400000000000000000000000167F56AFB00002209000000000000000000000000000000000000001700000000goipp-1.2.0/message.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP protocol messages
 */

package goipp

import (
	"bytes"
	"fmt"
	"io"
)

// Code represents Op(operation) or Status codes
type Code uint16

// Version represents a protocol version. It consist
// of Major and Minor version codes, packed into a single
// 16-bit word
type Version uint16

// DefaultVersion is the default IPP version (2.0 for now)
const DefaultVersion Version = 0x0200

// MakeVersion makes version from major and minor parts
func MakeVersion(major, minor uint8) Version {
	return Version(major)<<8 | Version(minor)
}

// Major returns a major part of version
func (v Version) Major() uint8 {
	return uint8(v >> 8)
}

// Minor returns a minor part of version
func (v Version) Minor() uint8 {
	return uint8(v)
}

// String() converts version to string (i.e., "2.0")
func (v Version) String() string {
	return fmt.Sprintf("%d.%d", v.Major(), v.Minor())
}

// Message represents a single IPP message, which may be either
// client request or server response
type Message struct {
	// Common header
	Version   Version // Protocol version
	Code      Code    // Operation for request, status for response
	RequestID uint32  // Set in request, returned in response

	// Groups of Attributes
	//
	// This field allows to represent messages with repeated
	// groups of attributes with the same group tag. The most
	// noticeable use case is the Get-Jobs response which uses
	// multiple Job groups, one per returned job. See RFC 8011,
	// 4.2.6.2. for more details
	//
	// See also the following discussions which explain the demand
	// to implement this interface:
	//   https://github.com/OpenPrinting/goipp/issues/2
	//   https://github.com/OpenPrinting/goipp/pull/3
	//
	// With respect to backward compatibility, the following
	// behavior is implemented here:
	//   1. (*Message).Decode() fills both Groups and named per-group
	//      fields (i.e., Operation, Job etc)
	//   2. (*Message).Encode() and (*Message) Print, if Groups != nil,
	//      uses Groups and ignores  named per-group fields. Otherwise,
	//      named fields are used as in 1.0.0
	//   3. (*Message) Equal(), for each message uses Groups if
	//      it is not nil or named per-group fields otherwise.
	//      In another words, Equal() compares messages as if
	//      they were encoded
	//
	// Since 1.1.0
	Groups Groups

	// Attributes, by group
	Operation         Attributes // Operation attributes
	Job               Attributes // Job attributes
	Printer           Attributes // Printer attributes
	Unsupported       Attributes // Unsupported attributes
	Subscription      Attributes // Subscription attributes
	EventNotification Attributes // Event Notification attributes
	Resource          Attributes // Resource attributes
	Document          Attributes // Document attributes
	System            Attributes // System attributes
	Future11          Attributes // \
	Future12          Attributes //  \
	Future13          Attributes //   | Reserved for future extensions
	Future14          Attributes //  /
	Future15          Attributes // /
}

// NewRequest creates a new request message
//
// Use DefaultVersion as a first argument, if you don't
// have any specific needs
func NewRequest(v Version, op Op, id uint32) *Message {
	return &Message{
		Version:   v,
		Code:      Code(op),
		RequestID: id,
	}
}

// NewResponse creates a new response message
//
// Use DefaultVersion as a first argument, if you don't
func NewResponse(v Version, status Status, id uint32) *Message {
	return &Message{
		Version:   v,
		Code:      Code(status),
		RequestID: id,
	}
}

// NewMessageWithGroups creates a new message with Groups of
// attributes.
//
// Fields like m.Operation, m.Job. m.Printer... and so on will
// be properly filled automatically.
func NewMessageWithGroups(v Version, code Code,
	id uint32, groups Groups) *Message {

	m := &Message{
		Version:   v,
		Code:      code,
		RequestID: id,
		Groups:    groups,
	}

	for _, grp := range m.Groups {
		switch grp.Tag {
		case TagOperationGroup:
			m.Operation = append(m.Operation, grp.Attrs...)
		case TagJobGroup:
			m.Job = append(m.Job, grp.Attrs...)
		case TagPrinterGroup:
			m.Printer = append(m.Printer, grp.Attrs...)
		case TagUnsupportedGroup:
			m.Unsupported = append(m.Unsupported, grp.Attrs...)
		case TagSubscriptionGroup:
			m.Subscription = append(m.Subscription, grp.Attrs...)
		case TagEventNotificationGroup:
			m.EventNotification = append(m.EventNotification,
				grp.Attrs...)
		case TagResourceGroup:
			m.Resource = append(m.Resource, grp.Attrs...)
		case TagDocumentGroup:
			m.Document = append(m.Document, grp.Attrs...)
		case TagSystemGroup:
			m.System = append(m.System, grp.Attrs...)
		case TagFuture11Group:
			m.Future11 = append(m.Future11, grp.Attrs...)
		case TagFuture12Group:
			m.Future12 = append(m.Future12, grp.Attrs...)
		case TagFuture13Group:
			m.Future13 = append(m.Future13, grp.Attrs...)
		case TagFuture14Group:
			m.Future14 = append(m.Future14, grp.Attrs...)
		case TagFuture15Group:
			m.Future15 = append(m.Future15, grp.Attrs...)
		}
	}

	return m
}

// Equal checks that two messages are equal
func (m Message) Equal(m2 Message) bool {
	if m.Version != m2.Version ||
		m.Code != m2.Code ||
		m.RequestID != m2.RequestID {
		return false
	}

	groups := m.AttrGroups()
	groups2 := m2.AttrGroups()

	return groups.Equal(groups2)
}

// Similar checks that two messages are **logically** equal,
// which means the following:
//   - Version, Code and RequestID are equal
//   - Groups of attributes are Similar
func (m Message) Similar(m2 Message) bool {
	if m.Version != m2.Version ||
		m.Code != m2.Code ||
		m.RequestID != m2.RequestID {
		return false
	}

	groups := m.AttrGroups()
	groups2 := m2.AttrGroups()

	return groups.Similar(groups2)
}

// Reset the message into initial state
func (m *Message) Reset() {
	*m = Message{}
}

// Encode message
func (m *Message) Encode(out io.Writer) error {
	me := messageEncoder{
		out: out,
	}

	return me.encode(m)
}

// EncodeBytes encodes message to byte slice
func (m *Message) EncodeBytes() ([]byte, error) {
	var buf bytes.Buffer

	err := m.Encode(&buf)
	return buf.Bytes(), err
}

// Decode reads message from io.Reader
func (m *Message) Decode(in io.Reader) error {
	return m.DecodeEx(in, DecoderOptions{})
}

// DecodeEx reads message from io.Reader
//
// It is extended version of the Decode method, with additional
// DecoderOptions parameter
func (m *Message) DecodeEx(in io.Reader, opt DecoderOptions) error {
	md := messageDecoder{
		in:  in,
		opt: opt,
	}

	m.Reset()
	return md.decode(m)
}

// DecodeBytes decodes message from byte slice
func (m *Message) DecodeBytes(data []byte) error {
	return m.Decode(bytes.NewBuffer(data))
}

// DecodeBytesEx decodes message from byte slice
//
// It is extended version of the DecodeBytes method, with additional
// DecoderOptions parameter
func (m *Message) DecodeBytesEx(data []byte, opt DecoderOptions) error {
	return m.DecodeEx(bytes.NewBuffer(data), opt)
}

// Print pretty-prints the message. The 'request' parameter affects
// interpretation of Message.Code: it is interpreted either
// as [Op] or as [Status].
//
// Deprecated. Use [Formatter] instead.
func (m *Message) Print(out io.Writer, request bool) {
	f := Formatter{}

	if request {
		f.FmtRequest(m)
	} else {
		f.FmtResponse(m)
	}

	f.WriteTo(out)
}

// AttrGroups returns [Message] attributes as a sequence of
// attribute groups.
//
// If [Message.Groups] is set, it will be returned.
//
// Otherwise, [Groups] will be reconstructed from [Message.Operation],
// [Message.Job], [Message.Printer] and so on.
//
// Groups with nil [Group.Attrs] will be skipped, but groups with non-nil
// will be not, even if len(Attrs) == 0
func (m *Message) AttrGroups() Groups {
	// If m.Groups is set, use it
	if m.Groups != nil {
		return m.Groups
	}

	// Initialize slice of groups
	groups := Groups{
		{TagOperationGroup, m.Operation},
		{TagJobGroup, m.Job},
		{TagPrinterGroup, m.Printer},
		{TagUnsupportedGroup, m.Unsupported},
		{TagSubscriptionGroup, m.Subscription},
		{TagEventNotificationGroup, m.EventNotification},
		{TagResourceGroup, m.Resource},
		{TagDocumentGroup, m.Document},
		{TagSystemGroup, m.System},
		{TagFuture11Group, m.Future11},
		{TagFuture12Group, m.Future12},
		{TagFuture13Group, m.Future13},
		{TagFuture14Group, m.Future14},
		{TagFuture15Group, m.Future15},
	}

	// Skip all empty groups
	out := 0
	for in := 0; in < len(groups); in++ {
		if groups[in].Attrs != nil {
			groups[out] = groups[in]
			out++
		}
	}

	return groups[:out]
}
07070100000013000081A400000000000000000000000167F56AFB000031D3000000000000000000000000000000000000001C00000000goipp-1.2.0/message_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Message tests
 */

package goipp

import (
	"bytes"
	"reflect"
	"strings"
	"testing"
)

// TestVersion tests Version functions
func TestVersion(t *testing.T) {
	type testData struct {
		major, minor uint8   // Major/minor parts
		ver          Version // Resulting version
		str          string  // Its string representation
	}

	tests := []testData{
		{
			major: 2,
			minor: 0,
			ver:   0x0200,
			str:   "2.0",
		},

		{
			major: 2,
			minor: 1,
			ver:   0x0201,
			str:   "2.1",
		},

		{
			major: 1,
			minor: 1,
			ver:   0x0101,
			str:   "1.1",
		},
	}

	for _, test := range tests {
		ver := MakeVersion(test.major, test.minor)
		if ver != test.ver {
			t.Errorf("MakeVersion test failed:\n"+
				"version expected: 0x%4.4x\n"+
				"version present:  0x%4.4x\n",
				uint(test.ver), uint(ver),
			)
			continue
		}

		str := ver.String()
		if str != test.str {
			t.Errorf("Version.String test failed:\n"+
				"expected: %s\n"+
				"present:  %s\n",
				test.str, str,
			)
			continue
		}

		major := ver.Major()
		if major != test.major {
			t.Errorf("Version.Major test failed:\n"+
				"expected: %d\n"+
				"present:  %d\n",
				test.major, major,
			)
			continue
		}

		minor := ver.Minor()
		if minor != test.minor {
			t.Errorf("Version.Minor test failed:\n"+
				"expected: %d\n"+
				"present:  %d\n",
				test.minor, minor,
			)
			continue
		}
	}
}

// TestNewRequestResponse tests NewRequest and NewResponse functions
func TestNewRequestResponse(t *testing.T) {
	msg := &Message{
		Version:   MakeVersion(2, 0),
		Code:      1,
		RequestID: 0x12345,
	}

	rq := NewRequest(msg.Version, Op(msg.Code), msg.RequestID)
	if !reflect.DeepEqual(msg, rq) {
		t.Errorf("NewRequest test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			msg, rq,
		)
	}

	rsp := NewResponse(msg.Version, Status(msg.Code), msg.RequestID)
	if !reflect.DeepEqual(msg, rsp) {
		t.Errorf("NewRequest test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			msg, rsp,
		)
	}
}

// TestNewMessageWithGroups tests the NewMessageWithGroups function.
func TestNewMessageWithGroups(t *testing.T) {
	// Populate groups
	ops := Group{
		TagOperationGroup,
		Attributes{
			MakeAttr("ops", TagInteger, Integer(1)),
		},
	}

	prn1 := Group{
		TagPrinterGroup,
		Attributes{
			MakeAttr("prn1", TagInteger, Integer(2)),
		},
	}

	prn2 := Group{
		TagPrinterGroup,
		Attributes{
			MakeAttr("prn2", TagInteger, Integer(3)),
		},
	}

	prn3 := Group{
		TagPrinterGroup,
		Attributes{
			MakeAttr("prn3", TagInteger, Integer(4)),
		},
	}

	job := Group{
		TagJobGroup,
		Attributes{
			MakeAttr("job", TagInteger, Integer(5)),
		},
	}

	unsupp := Group{
		TagUnsupportedGroup,
		Attributes{
			MakeAttr("unsupp", TagInteger, Integer(6)),
		},
	}

	sub := Group{
		TagSubscriptionGroup,
		Attributes{
			MakeAttr("sub", TagInteger, Integer(7)),
		},
	}

	evnt := Group{
		TagEventNotificationGroup,
		Attributes{
			MakeAttr("evnt", TagInteger, Integer(8)),
		},
	}

	res := Group{
		TagResourceGroup,
		Attributes{
			MakeAttr("res", TagInteger, Integer(9)),
		},
	}

	doc := Group{
		TagDocumentGroup,
		Attributes{
			MakeAttr("doc", TagInteger, Integer(10)),
		},
	}

	sys := Group{
		TagSystemGroup,
		Attributes{
			MakeAttr("sys", TagInteger, Integer(11)),
		},
	}

	future11 := Group{
		TagFuture11Group,
		Attributes{
			MakeAttr("future11", TagInteger, Integer(12)),
		},
	}

	future12 := Group{
		TagFuture12Group,
		Attributes{
			MakeAttr("future12", TagInteger, Integer(13)),
		},
	}

	future13 := Group{
		TagFuture13Group,
		Attributes{
			MakeAttr("future13", TagInteger, Integer(14)),
		},
	}

	future14 := Group{
		TagFuture14Group,
		Attributes{
			MakeAttr("future14", TagInteger, Integer(15)),
		},
	}

	future15 := Group{
		TagFuture15Group,
		Attributes{
			MakeAttr("future15", TagInteger, Integer(16)),
		},
	}

	groups := Groups{
		ops,
		prn1, prn2, prn3,
		job,
		unsupp,
		sub,
		evnt,
		res,
		doc,
		sys,
		future11,
		future12,
		future13,
		future14,
		future15,
	}

	msg := NewMessageWithGroups(DefaultVersion, 1, 123, groups)
	expected := &Message{
		Version:           DefaultVersion,
		Code:              1,
		RequestID:         123,
		Groups:            groups,
		Operation:         ops.Attrs,
		Job:               job.Attrs,
		Unsupported:       unsupp.Attrs,
		Subscription:      sub.Attrs,
		EventNotification: evnt.Attrs,
		Resource:          res.Attrs,
		Document:          doc.Attrs,
		System:            sys.Attrs,
		Future11:          future11.Attrs,
		Future12:          future12.Attrs,
		Future13:          future13.Attrs,
		Future14:          future14.Attrs,
		Future15:          future15.Attrs,
	}
	expected.Printer = prn1.Attrs
	expected.Printer = append(expected.Printer, prn2.Attrs...)
	expected.Printer = append(expected.Printer, prn3.Attrs...)

	if !reflect.DeepEqual(msg, expected) {
		t.Errorf("NewMessageWithGroups test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			expected,
			msg,
		)
	}
}

// TestNewMessageWithGroups tests the Message.AttrGroups function.
func TestMessageAttrGroups(t *testing.T) {
	// Create a message for testing
	uri := "ipp://192/168.0.1/ipp/print"

	m := NewRequest(DefaultVersion, OpCreateJob, 1)

	m.Operation.Add(MakeAttr("attributes-charset",
		TagCharset, String("utf-8")))
	m.Operation.Add(MakeAttr("attributes-natural-language",
		TagLanguage, String("en-US")))
	m.Operation.Add(MakeAttr("printer-uri",
		TagURI, String(uri)))

	m.Job.Add(MakeAttr("copies", TagInteger, Integer(1)))

	// Compare m.AttrGroups() with expectations
	groups := m.AttrGroups()
	expected := Groups{
		Group{
			Tag: TagOperationGroup,
			Attrs: Attributes{
				MakeAttr("attributes-charset",
					TagCharset, String("utf-8")),
				MakeAttr("attributes-natural-language",
					TagLanguage, String("en-US")),
				MakeAttr("printer-uri",
					TagURI, String(uri)),
			},
		},
		Group{
			Tag: TagJobGroup,
			Attrs: Attributes{
				MakeAttr("copies", TagInteger, Integer(1)),
			},
		},
	}

	if !reflect.DeepEqual(groups, expected) {
		t.Errorf("Message.AttrGroups test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			expected, groups,
		)
	}

	// Set m.Groups. Check that it takes precedence.
	expected = Groups{
		Group{
			Tag: TagOperationGroup,
			Attrs: Attributes{
				MakeAttr("attributes-charset",
					TagCharset, String("utf-8")),
			},
		},
	}

	m.Groups = expected
	groups = m.AttrGroups()

	if !reflect.DeepEqual(groups, expected) {
		t.Errorf("Message.AttrGroups test failed:\n"+
			"expected: %#v\n"+
			"present:  %#v\n",
			expected, groups,
		)
	}
}

// TestMessageEqualSimilar tests Message.Equal and Message.Similar functions.
func TestMessageEqualSimilar(t *testing.T) {
	type testData struct {
		m1, m2  Message // Input messages
		equal   bool    // Expected Message.Equal output
		similar bool    // Expected Message.Similar output
	}

	uri := "ipp://192/168.0.1/ipp/print"

	tests := []testData{
		// Empty messages are equal and similar
		{
			m1:      Message{},
			m2:      Message{},
			equal:   true,
			similar: true,
		},

		// Messages with different Version/Code/RequestID are
		// neither equal or similar
		{
			m1:      Message{},
			m2:      Message{Version: 1},
			equal:   false,
			similar: false,
		},

		{
			m1:      Message{},
			m2:      Message{Code: 1},
			equal:   false,
			similar: false,
		},

		{
			m1:      Message{},
			m2:      Message{RequestID: 1},
			equal:   false,
			similar: false,
		},

		// If the same attributes represented as Message.Groups in one
		// message and via Message.Operation/Job/Printer etc in the
		// another message, these messages are equal and similar
		{
			m1: Message{
				Groups: Groups{
					Group{
						Tag: TagOperationGroup,
						Attrs: Attributes{
							MakeAttr("attributes-charset",
								TagCharset, String("utf-8")),
							MakeAttr("attributes-natural-language",
								TagLanguage, String("en-US")),
							MakeAttr("printer-uri",
								TagURI, String(uri)),
						},
					},
					Group{
						Tag: TagJobGroup,
						Attrs: Attributes{
							MakeAttr("copies", TagInteger, Integer(1)),
						},
					},
				},
			},

			m2: Message{
				Operation: Attributes{
					MakeAttr("attributes-charset",
						TagCharset, String("utf-8")),
					MakeAttr("attributes-natural-language",
						TagLanguage, String("en-US")),
					MakeAttr("printer-uri",
						TagURI, String(uri)),
				},

				Job: Attributes{
					MakeAttr("copies", TagInteger, Integer(1)),
				},
			},

			equal:   true,
			similar: true,
		},

		// Messages with the different order of the same set of attributes
		// are similar but not equal.
		{
			m1: Message{
				Operation: Attributes{
					MakeAttr("attributes-charset",
						TagCharset, String("utf-8")),
					MakeAttr("attributes-natural-language",
						TagLanguage, String("en-US")),
					MakeAttr("printer-uri",
						TagURI, String(uri)),
				},
			},

			m2: Message{
				Operation: Attributes{
					MakeAttr("attributes-charset",
						TagCharset, String("utf-8")),
					MakeAttr("printer-uri",
						TagURI, String(uri)),
					MakeAttr("attributes-natural-language",
						TagLanguage, String("en-US")),
				},
			},

			equal:   false,
			similar: true,
		},
	}

	for _, test := range tests {
		equal := test.m1.Equal(test.m2)
		if equal != test.equal {
			var buf1, buf2 bytes.Buffer
			test.m1.Print(&buf1, true)
			test.m2.Print(&buf2, true)

			t.Errorf("testing Message.Equal:\n"+
				"message 1: %s\n"+
				"message 2: %s\n"+
				"expected:  %v\n"+
				"present:   %v\n",
				&buf1, &buf2,
				test.equal, equal,
			)
		}

		similar := test.m1.Similar(test.m2)
		if similar != test.similar {
			var buf1, buf2 bytes.Buffer
			test.m1.Print(&buf1, true)
			test.m2.Print(&buf2, true)

			t.Errorf("testing Message.Similar:\n"+
				"message 1: %s\n"+
				"message 2: %s\n"+
				"expected:  %v\n"+
				"present:   %v\n",
				&buf1, &buf2,
				test.similar, similar,
			)
		}
	}
}

// TestMessageReset tests Message.Reset function
func TestMessageReset(t *testing.T) {
	uri := "ipp://192/168.0.1/ipp/print"
	m := Message{
		Groups: Groups{
			Group{
				Tag: TagOperationGroup,
				Attrs: Attributes{
					MakeAttr("attributes-charset",
						TagCharset, String("utf-8")),
					MakeAttr("attributes-natural-language",
						TagLanguage, String("en-US")),
					MakeAttr("printer-uri",
						TagURI, String(uri)),
				},
			},
			Group{
				Tag: TagJobGroup,
				Attrs: Attributes{
					MakeAttr("copies", TagInteger, Integer(1)),
				},
			},
		},
	}

	m.Reset()

	if !reflect.ValueOf(m).IsZero() {
		t.Errorf("Message.Reset test failed")
	}
}

// TestMessagePrint tests Message.Print function
func TestMessagePrint(t *testing.T) {
	uri := "ipp://192/168.0.1/ipp/print"
	m := Message{
		Code:      2,
		Version:   MakeVersion(2, 0),
		RequestID: 1,
		Groups: Groups{
			Group{
				Tag: TagOperationGroup,
				Attrs: Attributes{
					MakeAttr("attributes-charset",
						TagCharset, String("utf-8")),
					MakeAttr("attributes-natural-language",
						TagLanguage, String("en-US")),
					MakeAttr("printer-uri",
						TagURI, String(uri)),
				},
			},
			Group{
				Tag: TagJobGroup,
				Attrs: Attributes{
					MakeAttr("copies", TagInteger, Integer(1)),
				},
			},
		},
	}

	// Check request formatting
	reqExpected := []string{
		`{`,
		`    REQUEST-ID 1`,
		`    VERSION 2.0`,
		`    OPERATION Print-Job`,
		``,
		`    GROUP operation-attributes-tag`,
		`    ATTR "attributes-charset" charset: utf-8`,
		`    ATTR "attributes-natural-language" naturalLanguage: en-US`,
		`    ATTR "printer-uri" uri: ipp://192/168.0.1/ipp/print`,
		``,
		`    GROUP job-attributes-tag`,
		`    ATTR "copies" integer: 1`,
		`}`,
	}

	var buf bytes.Buffer
	m.Print(&buf, true)
	exp := strings.Join(reqExpected, "\n") + "\n"

	if buf.String() != exp {
		t.Errorf("Message.Print test failed for request:\n"+
			"expected: %s\n"+
			"present:  %s\n",
			exp, &buf,
		)
	}

	// Check response formatting
	rspExpected := []string{
		`{`,
		`    REQUEST-ID 1`,
		`    VERSION 2.0`,
		`    STATUS successful-ok`,
		``,
		`    GROUP operation-attributes-tag`,
		`    ATTR "attributes-charset" charset: utf-8`,
		`    ATTR "attributes-natural-language" naturalLanguage: en-US`,
		`    ATTR "printer-uri" uri: ipp://192/168.0.1/ipp/print`,
		``,
		`    GROUP job-attributes-tag`,
		`    ATTR "copies" integer: 1`,
		`}`,
	}

	buf.Reset()
	m.Code = 0
	m.Print(&buf, false)
	exp = strings.Join(rspExpected, "\n") + "\n"

	if buf.String() != exp {
		t.Errorf("Message.Print test failed for response:\n"+
			"expected: %s\n"+
			"present:  %s\n",
			exp, &buf,
		)
	}
}
07070100000014000081A400000000000000000000000167F56AFB00004461000000000000000000000000000000000000001200000000goipp-1.2.0/op.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Operation Codes
 */

package goipp

import (
	"fmt"
)

// Op represents an IPP Operation Code
type Op Code

// Op codes
const (
	OpPrintJob             Op = 0x0002 // Print-Job: Print a single file
	OpPrintURI             Op = 0x0003 // Print-URI: Print a single URL
	OpValidateJob          Op = 0x0004 // Validate-Job: Validate job values prior to submission
	OpCreateJob            Op = 0x0005 // Create-Job: Create an empty print job
	OpSendDocument         Op = 0x0006 // Send-Document: Add a file to a job
	OpSendURI              Op = 0x0007 // Send-URI: Add a URL to a job
	OpCancelJob            Op = 0x0008 // Cancel-Job: Cancel a job
	OpGetJobAttributes     Op = 0x0009 // Get-Job-Attribute: Get information about a job
	OpGetJobs              Op = 0x000a // Get-Jobs: Get a list of jobs
	OpGetPrinterAttributes Op = 0x000b // Get-Printer-Attributes: Get information about a printer
	OpHoldJob              Op = 0x000c // Hold-Job: Hold a job for printing
	OpReleaseJob           Op = 0x000d // Release-Job: Release a job for printing
	OpRestartJob           Op = 0x000e // Restart-Job: Reprint a job

	OpPausePrinter               Op = 0x0010 // Pause-Printer: Stop a printer
	OpResumePrinter              Op = 0x0011 // Resume-Printer: Start a printer
	OpPurgeJobs                  Op = 0x0012 // Purge-Jobs: Delete all jobs
	OpSetPrinterAttributes       Op = 0x0013 // Set-Printer-Attributes: Set printer values
	OpSetJobAttributes           Op = 0x0014 // Set-Job-Attributes: Set job values
	OpGetPrinterSupportedValues  Op = 0x0015 // Get-Printer-Supported-Values: Get supported values
	OpCreatePrinterSubscriptions Op = 0x0016 // Create-Printer-Subscriptions: Create one or more printer subscriptions
	OpCreateJobSubscriptions     Op = 0x0017 // Create-Job-Subscriptions: Create one of more job subscriptions
	OpGetSubscriptionAttributes  Op = 0x0018 // Get-Subscription-Attributes: Get subscription information
	OpGetSubscriptions           Op = 0x0019 // Get-Subscriptions: Get list of subscriptions
	OpRenewSubscription          Op = 0x001a // Renew-Subscription: Renew a printer subscription
	OpCancelSubscription         Op = 0x001b // Cancel-Subscription: Cancel a subscription
	OpGetNotifications           Op = 0x001c // Get-Notifications: Get notification events
	OpSendNotifications          Op = 0x001d // Send-Notifications: Send notification events
	OpGetResourceAttributes      Op = 0x001e // Get-Resource-Attributes: Get resource information
	OpGetResourceData            Op = 0x001f // Get-Resource-Data: Get resource data

	OpGetResources                Op = 0x0020 // Get-Resources: Get list of resources
	OpGetPrintSupportFiles        Op = 0x0021 // Get-Printer-Support-Files: Get printer support files
	OpEnablePrinter               Op = 0x0022 // Enable-Printer: Accept new jobs for a printer
	OpDisablePrinter              Op = 0x0023 // Disable-Printer: Reject new jobs for a printer
	OpPausePrinterAfterCurrentJob Op = 0x0024 // Pause-Printer-After-Current-Job: Stop printer after the current job
	OpHoldNewJobs                 Op = 0x0025 // Hold-New-Jobs: Hold new jobs
	OpReleaseHeldNewJobs          Op = 0x0026 // Release-Held-New-Jobs: Release new jobs that were previously held
	OpDeactivatePrinter           Op = 0x0027 // Deactivate-Printer: Stop a printer and do not accept jobs
	OpActivatePrinter             Op = 0x0028 // Activate-Printer: Start a printer and accept jobs
	OpRestartPrinter              Op = 0x0029 // Restart-Printer: Restart a printer
	OpShutdownPrinter             Op = 0x002a // Shutdown-Printer: Turn a printer off
	OpStartupPrinter              Op = 0x002b // Startup-Printer: Turn a printer on
	OpReprocessJob                Op = 0x002c // Reprocess-Job: Reprint a job
	OpCancelCurrentJob            Op = 0x002d // Cancel-Current-Job: Cancel the current job
	OpSuspendCurrentJob           Op = 0x002e // Suspend-Current-Job: Suspend the current job
	OpResumeJob                   Op = 0x002f // Resume-Job: Resume the current job

	OpPromoteJob            Op = 0x0030 // Promote-Job: Promote a job to print sooner
	OpScheduleJobAfter      Op = 0x0031 // Schedule-Job-After: Schedule a job to print after another
	OpCancelDocument        Op = 0x0033 // Cancel-Document: Cancel a document
	OpGetDocumentAttributes Op = 0x0034 // Get-Document-Attributes: Get document information
	OpGetDocuments          Op = 0x0035 // Get-Documents: Get a list of documents in a job
	OpDeleteDocument        Op = 0x0036 // Delete-Document: Delete a document
	OpSetDocumentAttributes Op = 0x0037 // Set-Document-Attributes: Set document values
	OpCancelJobs            Op = 0x0038 // Cancel-Jobs: Cancel all jobs (administrative)
	OpCancelMyJobs          Op = 0x0039 // Cancel-My-Jobs: Cancel a user's jobs
	OpResubmitJob           Op = 0x003a // Resubmit-Job: Copy and reprint a job
	OpCloseJob              Op = 0x003b // Close-Job: Close a job and start printing
	OpIdentifyPrinter       Op = 0x003c // Identify-Printer: Make the printer beep, flash, or display a message for identification
	OpValidateDocument      Op = 0x003d // Validate-Document: Validate document values prior to submission
	OpAddDocumentImages     Op = 0x003e // Add-Document-Images: Add image(s) from the specified scanner source
	OpAcknowledgeDocument   Op = 0x003f // Acknowledge-Document: Acknowledge processing of a document

	OpAcknowledgeIdentifyPrinter   Op = 0x0040 // Acknowledge-Identify-Printer: Acknowledge action on an Identify-Printer request
	OpAcknowledgeJob               Op = 0x0041 // Acknowledge-Job: Acknowledge processing of a job
	OpFetchDocument                Op = 0x0042 // Fetch-Document: Fetch a document for processing
	OpFetchJob                     Op = 0x0043 // Fetch-Job: Fetch a job for processing
	OpGetOutputDeviceAttributes    Op = 0x0044 // Get-Output-Device-Attributes: Get printer information for a specific output device
	OpUpdateActiveJobs             Op = 0x0045 // Update-Active-Jobs: Update the list of active jobs that a proxy has processed
	OpDeregisterOutputDevice       Op = 0x0046 // Deregister-Output-Device: Remove an output device
	OpUpdateDocumentStatus         Op = 0x0047 // Update-Document-Status: Update document values
	OpUpdateJobStatus              Op = 0x0048 // Update-Job-Status: Update job values
	OpupdateOutputDeviceAttributes Op = 0x0049 // Update-Output-Device-Attributes: Update output device values
	OpGetNextDocumentData          Op = 0x004a // Get-Next-Document-Data: Scan more document data
	OpAllocatePrinterResources     Op = 0x004b // Allocate-Printer-Resources: Use resources for a printer
	OpCreatePrinter                Op = 0x004c // Create-Printer: Create a new service
	OpDeallocatePrinterResources   Op = 0x004d // Deallocate-Printer-Resources: Stop using resources for a printer
	OpDeletePrinter                Op = 0x004e // Delete-Printer: Delete an existing service
	OpGetPrinters                  Op = 0x004f // Get-Printers: Get a list of services

	OpShutdownOnePrinter              Op = 0x0050 // Shutdown-One-Printer: Shutdown a service
	OpStartupOnePrinter               Op = 0x0051 // Startup-One-Printer: Start a service
	OpCancelResource                  Op = 0x0052 // Cancel-Resource: Uninstall a resource
	OpCreateResource                  Op = 0x0053 // Create-Resource: Create a new (empty) resource
	OpInstallResource                 Op = 0x0054 // Install-Resource: Install a resource
	OpSendResourceData                Op = 0x0055 // Send-Resource-Data: Upload the data for a resource
	OpSetResourceAttributes           Op = 0x0056 // Set-Resource-Attributes: Set resource object  attributes
	OpCreateResourceSubscriptions     Op = 0x0057 // Create-Resource-Subscriptions: Create event subscriptions for a resource
	OpCreateSystemSubscriptions       Op = 0x0058 // Create-System-Subscriptions: Create event subscriptions for a system
	OpDisableAllPrinters              Op = 0x0059 // Disable-All-Printers: Stop accepting new jobs on all services
	OpEnableAllPrinters               Op = 0x005a // Enable-All-Printers: Start accepting new jobs on all services
	OpGetSystemAttributes             Op = 0x005b // Get-System-Attributes: Get system object attributes
	OpGetSystemSupportedValues        Op = 0x005c // Get-System-Supported-Values: Get supported values for system object attributes
	OpPauseAllPrinters                Op = 0x005d // Pause-All-Printers: Stop all services immediately
	OpPauseAllPrintersAfterCurrentJob Op = 0x005e // Pause-All-Printers-After-Current-Job: Stop all services after processing the current jobs
	OpRegisterOutputDevice            Op = 0x005f // Register-Output-Device: Register a remote service

	OpRestartSystem       Op = 0x0060 // Restart-System: Restart all services
	OpResumeAllPrinters   Op = 0x0061 // Resume-All-Printers: Start job processing on all services
	OpSetSystemAttributes Op = 0x0062 // Set-System-Attributes: Set system object attributes
	OpShutdownAllPrinters Op = 0x0063 // Shutdown-All-Printers: Shutdown all services
	OpStartupAllPrinters  Op = 0x0064 // Startup-All-Printers: Startup all services

	OpCupsGetDefault       Op = 0x4001 // CUPS-Get-Default: Get the default printer
	OpCupsGetPrinters      Op = 0x4002 // CUPS-Get-Printers: Get a list of printers and/or classes
	OpCupsAddModifyPrinter Op = 0x4003 // CUPS-Add-Modify-Printer: Add or modify a printer
	OpCupsDeletePrinter    Op = 0x4004 // CUPS-Delete-Printer: Delete a printer
	OpCupsGetClasses       Op = 0x4005 // CUPS-Get-Classes: Get a list of classes
	OpCupsAddModifyClass   Op = 0x4006 // CUPS-Add-Modify-Class: Add or modify a class
	OpCupsDeleteClass      Op = 0x4007 // CUPS-Delete-Class: Delete a class
	OpCupsAcceptJobs       Op = 0x4008 // CUPS-Accept-Jobs: Accept new jobs on a printer
	OpCupsRejectJobs       Op = 0x4009 // CUPS-Reject-Jobs: Reject new jobs on a printer
	OpCupsSetDefault       Op = 0x400a // CUPS-Set-Default: Set the default printer
	OpCupsGetDevices       Op = 0x400b // CUPS-Get-Devices: Get a list of supported devices
	OpCupsGetPpds          Op = 0x400c // CUPS-Get-PPDs: Get a list of supported drivers
	OpCupsMoveJob          Op = 0x400d // CUPS-Move-Job: Move a job to a different printer
	OpCupsAuthenticateJob  Op = 0x400e // CUPS-Authenticate-Job: Authenticate a job
	OpCupsGetPpd           Op = 0x400f // CUPS-Get-PPD: Get a PPD file

	OpCupsGetDocument        Op = 0x4027 // CUPS-Get-Document: Get a document file
	OpCupsCreateLocalPrinter Op = 0x4028 // CUPS-Create-Local-Printer: Create a local (temporary) printer

)

// String() returns a Status name, as defined by RFC 8010
func (op Op) String() string {
	if s := opNames[op]; s != "" {
		return s
	}

	return fmt.Sprintf("0x%4.4x", int(op))
}

var opNames = map[Op]string{
	OpPrintJob:                        "Print-Job",
	OpPrintURI:                        "Print-URI",
	OpValidateJob:                     "Validate-Job",
	OpCreateJob:                       "Create-Job",
	OpSendDocument:                    "Send-Document",
	OpSendURI:                         "Send-URI",
	OpCancelJob:                       "Cancel-Job",
	OpGetJobAttributes:                "Get-Job-Attribute",
	OpGetJobs:                         "Get-Jobs",
	OpGetPrinterAttributes:            "Get-Printer-Attributes",
	OpHoldJob:                         "Hold-Job",
	OpReleaseJob:                      "Release-Job",
	OpRestartJob:                      "Restart-Job",
	OpPausePrinter:                    "Pause-Printer",
	OpResumePrinter:                   "Resume-Printer",
	OpPurgeJobs:                       "Purge-Jobs",
	OpSetPrinterAttributes:            "Set-Printer-Attributes",
	OpSetJobAttributes:                "Set-Job-Attributes",
	OpGetPrinterSupportedValues:       "Get-Printer-Supported-Values",
	OpCreatePrinterSubscriptions:      "Create-Printer-Subscriptions",
	OpCreateJobSubscriptions:          "Create-Job-Subscriptions",
	OpGetSubscriptionAttributes:       "Get-Subscription-Attributes",
	OpGetSubscriptions:                "Get-Subscriptions",
	OpRenewSubscription:               "Renew-Subscription",
	OpCancelSubscription:              "Cancel-Subscription",
	OpGetNotifications:                "Get-Notifications",
	OpSendNotifications:               "Send-Notifications",
	OpGetResourceAttributes:           "Get-Resource-Attributes",
	OpGetResourceData:                 "Get-Resource-Data",
	OpGetResources:                    "Get-Resources",
	OpGetPrintSupportFiles:            "Get-Printer-Support-Files",
	OpEnablePrinter:                   "Enable-Printer",
	OpDisablePrinter:                  "Disable-Printer",
	OpPausePrinterAfterCurrentJob:     "Pause-Printer-After-Current-Job",
	OpHoldNewJobs:                     "Hold-New-Jobs",
	OpReleaseHeldNewJobs:              "Release-Held-New-Jobs",
	OpDeactivatePrinter:               "Deactivate-Printer",
	OpActivatePrinter:                 "Activate-Printer",
	OpRestartPrinter:                  "Restart-Printer",
	OpShutdownPrinter:                 "Shutdown-Printer",
	OpStartupPrinter:                  "Startup-Printer",
	OpReprocessJob:                    "Reprocess-Job",
	OpCancelCurrentJob:                "Cancel-Current-Job",
	OpSuspendCurrentJob:               "Suspend-Current-Job",
	OpResumeJob:                       "Resume-Job",
	OpPromoteJob:                      "Promote-Job",
	OpScheduleJobAfter:                "Schedule-Job-After",
	OpCancelDocument:                  "Cancel-Document",
	OpGetDocumentAttributes:           "Get-Document-Attributes",
	OpGetDocuments:                    "Get-Documents",
	OpDeleteDocument:                  "Delete-Document",
	OpSetDocumentAttributes:           "Set-Document-Attributes",
	OpCancelJobs:                      "Cancel-Jobs",
	OpCancelMyJobs:                    "Cancel-My-Jobs",
	OpResubmitJob:                     "Resubmit-Job",
	OpCloseJob:                        "Close-Job",
	OpIdentifyPrinter:                 "Identify-Printer",
	OpValidateDocument:                "Validate-Document",
	OpAddDocumentImages:               "Add-Document-Images",
	OpAcknowledgeDocument:             "Acknowledge-Document",
	OpAcknowledgeIdentifyPrinter:      "Acknowledge-Identify-Printer",
	OpAcknowledgeJob:                  "Acknowledge-Job",
	OpFetchDocument:                   "Fetch-Document",
	OpFetchJob:                        "Fetch-Job",
	OpGetOutputDeviceAttributes:       "Get-Output-Device-Attributes",
	OpUpdateActiveJobs:                "Update-Active-Jobs",
	OpDeregisterOutputDevice:          "Deregister-Output-Device",
	OpUpdateDocumentStatus:            "Update-Document-Status",
	OpUpdateJobStatus:                 "Update-Job-Status",
	OpupdateOutputDeviceAttributes:    "Update-Output-Device-Attributes",
	OpGetNextDocumentData:             "Get-Next-Document-Data",
	OpAllocatePrinterResources:        "Allocate-Printer-Resources",
	OpCreatePrinter:                   "Create-Printer",
	OpDeallocatePrinterResources:      "Deallocate-Printer-Resources",
	OpDeletePrinter:                   "Delete-Printer",
	OpGetPrinters:                     "Get-Printers",
	OpShutdownOnePrinter:              "Shutdown-One-Printer",
	OpStartupOnePrinter:               "Startup-One-Printer",
	OpCancelResource:                  "Cancel-Resource",
	OpCreateResource:                  "Create-Resource",
	OpInstallResource:                 "Install-Resource",
	OpSendResourceData:                "Send-Resource-Data",
	OpSetResourceAttributes:           "Set-Resource-Attributes",
	OpCreateResourceSubscriptions:     "Create-Resource-Subscriptions",
	OpCreateSystemSubscriptions:       "Create-System-Subscriptions",
	OpDisableAllPrinters:              "Disable-All-Printers",
	OpEnableAllPrinters:               "Enable-All-Printers",
	OpGetSystemAttributes:             "Get-System-Attributes",
	OpGetSystemSupportedValues:        "Get-System-Supported-Values",
	OpPauseAllPrinters:                "Pause-All-Printers",
	OpPauseAllPrintersAfterCurrentJob: "Pause-All-Printers-After-Current-Job",
	OpRegisterOutputDevice:            "Register-Output-Device",
	OpRestartSystem:                   "Restart-System",
	OpResumeAllPrinters:               "Resume-All-Printers",
	OpSetSystemAttributes:             "Set-System-Attributes",
	OpShutdownAllPrinters:             "Shutdown-All-Printers",
	OpStartupAllPrinters:              "Startup-All-Printers",
	OpCupsGetDefault:                  "CUPS-Get-Default",
	OpCupsGetPrinters:                 "CUPS-Get-Printers",
	OpCupsAddModifyPrinter:            "CUPS-Add-Modify-Printer",
	OpCupsDeletePrinter:               "CUPS-Delete-Printer",
	OpCupsGetClasses:                  "CUPS-Get-Classes",
	OpCupsAddModifyClass:              "CUPS-Add-Modify-Class",
	OpCupsDeleteClass:                 "CUPS-Delete-Class",
	OpCupsAcceptJobs:                  "CUPS-Accept-Jobs",
	OpCupsRejectJobs:                  "CUPS-Reject-Jobs",
	OpCupsSetDefault:                  "CUPS-Set-Default",
	OpCupsGetDevices:                  "CUPS-Get-Devices",
	OpCupsGetPpds:                     "CUPS-Get-PPDs",
	OpCupsMoveJob:                     "CUPS-Move-Job",
	OpCupsAuthenticateJob:             "CUPS-Authenticate-Job",
	OpCupsGetPpd:                      "CUPS-Get-PPD",
	OpCupsGetDocument:                 "CUPS-Get-Document",
	OpCupsCreateLocalPrinter:          "CUPS-Create-Local-Printer",
}
07070100000015000081A400000000000000000000000167F56AFB000003BA000000000000000000000000000000000000001700000000goipp-1.2.0/op_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Operation Codes tests
 */

package goipp

import "testing"

// TestOpString tests Op.String method
func TestOpString(t *testing.T) {
	type testData struct {
		op Op     // Input Op code
		s  string // Expected output string
	}

	tests := []testData{
		{OpPrintJob, "Print-Job"},
		{OpPrintURI, "Print-URI"},
		{OpPausePrinter, "Pause-Printer"},
		{OpRestartSystem, "Restart-System"},
		{OpCupsGetDefault, "CUPS-Get-Default"},
		{OpCupsGetPpd, "CUPS-Get-PPD"},
		{OpCupsCreateLocalPrinter, "CUPS-Create-Local-Printer"},
		{0xabcd, "0xabcd"},
	}

	for _, test := range tests {
		s := test.op.String()
		if s != test.s {
			t.Errorf("testing Op.String:\n"+
				"input:    0x%4.4x\n"+
				"expected: %s\n"+
				"present:  %s\n",
				int(test.op), test.s, s,
			)
		}
	}
}
07070100000016000081A400000000000000000000000167F56AFB000026A1000000000000000000000000000000000000001600000000goipp-1.2.0/status.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Status Codes
 */

package goipp

import (
	"fmt"
)

// Status represents an IPP Status Code
type Status Code

// Status codes
const (
	StatusOk                              Status = 0x0000 // successful-ok
	StatusOkIgnoredOrSubstituted          Status = 0x0001 // successful-ok-ignored-or-substituted-attributes
	StatusOkConflicting                   Status = 0x0002 // successful-ok-conflicting-attributes
	StatusOkIgnoredSubscriptions          Status = 0x0003 // successful-ok-ignored-subscriptions
	StatusOkIgnoredNotifications          Status = 0x0004 // successful-ok-ignored-notifications
	StatusOkTooManyEvents                 Status = 0x0005 // successful-ok-too-many-events
	StatusOkButCancelSubscription         Status = 0x0006 // successful-ok-but-cancel-subscription
	StatusOkEventsComplete                Status = 0x0007 // successful-ok-events-complete
	StatusRedirectionOtherSite            Status = 0x0200 // redirection-other-site
	StatusCupsSeeOther                    Status = 0x0280 // cups-see-other
	StatusErrorBadRequest                 Status = 0x0400 // client-error-bad-request
	StatusErrorForbidden                  Status = 0x0401 // client-error-forbidden
	StatusErrorNotAuthenticated           Status = 0x0402 // client-error-not-authenticated
	StatusErrorNotAuthorized              Status = 0x0403 // client-error-not-authorized
	StatusErrorNotPossible                Status = 0x0404 // client-error-not-possible
	StatusErrorTimeout                    Status = 0x0405 // client-error-timeout
	StatusErrorNotFound                   Status = 0x0406 // client-error-not-found
	StatusErrorGone                       Status = 0x0407 // client-error-gone
	StatusErrorRequestEntity              Status = 0x0408 // client-error-request-entity-too-large
	StatusErrorRequestValue               Status = 0x0409 // client-error-request-value-too-long
	StatusErrorDocumentFormatNotSupported Status = 0x040a // client-error-document-format-not-supported
	StatusErrorAttributesOrValues         Status = 0x040b // client-error-attributes-or-values-not-supported
	StatusErrorURIScheme                  Status = 0x040c // client-error-uri-scheme-not-supported
	StatusErrorCharset                    Status = 0x040d // client-error-charset-not-supported
	StatusErrorConflicting                Status = 0x040e // client-error-conflicting-attributes
	StatusErrorCompressionNotSupported    Status = 0x040f // client-error-compression-not-supported
	StatusErrorCompressionError           Status = 0x0410 // client-error-compression-error
	StatusErrorDocumentFormatError        Status = 0x0411 // client-error-document-format-error
	StatusErrorDocumentAccess             Status = 0x0412 // client-error-document-access-error
	StatusErrorAttributesNotSettable      Status = 0x0413 // client-error-attributes-not-settable
	StatusErrorIgnoredAllSubscriptions    Status = 0x0414 // client-error-ignored-all-subscriptions
	StatusErrorTooManySubscriptions       Status = 0x0415 // client-error-too-many-subscriptions
	StatusErrorIgnoredAllNotifications    Status = 0x0416 // client-error-ignored-all-notifications
	StatusErrorPrintSupportFileNotFound   Status = 0x0417 // client-error-print-support-file-not-found
	StatusErrorDocumentPassword           Status = 0x0418 // client-error-document-password-error
	StatusErrorDocumentPermission         Status = 0x0419 // client-error-document-permission-error
	StatusErrorDocumentSecurity           Status = 0x041a // client-error-document-security-error
	StatusErrorDocumentUnprintable        Status = 0x041b // client-error-document-unprintable-error
	StatusErrorAccountInfoNeeded          Status = 0x041c // client-error-account-info-needed
	StatusErrorAccountClosed              Status = 0x041d // client-error-account-closed
	StatusErrorAccountLimitReached        Status = 0x041e // client-error-account-limit-reached
	StatusErrorAccountAuthorizationFailed Status = 0x041f // client-error-account-authorization-failed
	StatusErrorNotFetchable               Status = 0x0420 // client-error-not-fetchable
	StatusErrorInternal                   Status = 0x0500 // server-error-internal-error
	StatusErrorOperationNotSupported      Status = 0x0501 // server-error-operation-not-supported
	StatusErrorServiceUnavailable         Status = 0x0502 // server-error-service-unavailable
	StatusErrorVersionNotSupported        Status = 0x0503 // server-error-version-not-supported
	StatusErrorDevice                     Status = 0x0504 // server-error-device-error
	StatusErrorTemporary                  Status = 0x0505 // server-error-temporary-error
	StatusErrorNotAcceptingJobs           Status = 0x0506 // server-error-not-accepting-jobs
	StatusErrorBusy                       Status = 0x0507 // server-error-busy
	StatusErrorJobCanceled                Status = 0x0508 // server-error-job-canceled
	StatusErrorMultipleJobsNotSupported   Status = 0x0509 // server-error-multiple-document-jobs-not-supported
	StatusErrorPrinterIsDeactivated       Status = 0x050a // server-error-printer-is-deactivated
	StatusErrorTooManyJobs                Status = 0x050b // server-error-too-many-jobs
	StatusErrorTooManyDocuments           Status = 0x050c // server-error-too-many-documents
)

// String() returns a Status name, as defined by RFC 8010
func (status Status) String() string {
	if s := statusNames[status]; s != "" {
		return s
	}

	return fmt.Sprintf("0x%4.4x", int(status))
}

var statusNames = map[Status]string{
	StatusOk:                              "successful-ok",
	StatusOkIgnoredOrSubstituted:          "successful-ok-ignored-or-substituted-attributes",
	StatusOkConflicting:                   "successful-ok-conflicting-attributes",
	StatusOkIgnoredSubscriptions:          "successful-ok-ignored-subscriptions",
	StatusOkIgnoredNotifications:          "successful-ok-ignored-notifications",
	StatusOkTooManyEvents:                 "successful-ok-too-many-events",
	StatusOkButCancelSubscription:         "successful-ok-but-cancel-subscription",
	StatusOkEventsComplete:                "successful-ok-events-complete",
	StatusRedirectionOtherSite:            "redirection-other-site",
	StatusCupsSeeOther:                    "cups-see-other",
	StatusErrorBadRequest:                 "client-error-bad-request",
	StatusErrorForbidden:                  "client-error-forbidden",
	StatusErrorNotAuthenticated:           "client-error-not-authenticated",
	StatusErrorNotAuthorized:              "client-error-not-authorized",
	StatusErrorNotPossible:                "client-error-not-possible",
	StatusErrorTimeout:                    "client-error-timeout",
	StatusErrorNotFound:                   "client-error-not-found",
	StatusErrorGone:                       "client-error-gone",
	StatusErrorRequestEntity:              "client-error-request-entity-too-large",
	StatusErrorRequestValue:               "client-error-request-value-too-long",
	StatusErrorDocumentFormatNotSupported: "client-error-document-format-not-supported",
	StatusErrorAttributesOrValues:         "client-error-attributes-or-values-not-supported",
	StatusErrorURIScheme:                  "client-error-uri-scheme-not-supported",
	StatusErrorCharset:                    "client-error-charset-not-supported",
	StatusErrorConflicting:                "client-error-conflicting-attributes",
	StatusErrorCompressionNotSupported:    "client-error-compression-not-supported",
	StatusErrorCompressionError:           "client-error-compression-error",
	StatusErrorDocumentFormatError:        "client-error-document-format-error",
	StatusErrorDocumentAccess:             "client-error-document-access-error",
	StatusErrorAttributesNotSettable:      "client-error-attributes-not-settable",
	StatusErrorIgnoredAllSubscriptions:    "client-error-ignored-all-subscriptions",
	StatusErrorTooManySubscriptions:       "client-error-too-many-subscriptions",
	StatusErrorIgnoredAllNotifications:    "client-error-ignored-all-notifications",
	StatusErrorPrintSupportFileNotFound:   "client-error-print-support-file-not-found",
	StatusErrorDocumentPassword:           "client-error-document-password-error",
	StatusErrorDocumentPermission:         "client-error-document-permission-error",
	StatusErrorDocumentSecurity:           "client-error-document-security-error",
	StatusErrorDocumentUnprintable:        "client-error-document-unprintable-error",
	StatusErrorAccountInfoNeeded:          "client-error-account-info-needed",
	StatusErrorAccountClosed:              "client-error-account-closed",
	StatusErrorAccountLimitReached:        "client-error-account-limit-reached",
	StatusErrorAccountAuthorizationFailed: "client-error-account-authorization-failed",
	StatusErrorNotFetchable:               "client-error-not-fetchable",
	StatusErrorInternal:                   "server-error-internal-error",
	StatusErrorOperationNotSupported:      "server-error-operation-not-supported",
	StatusErrorServiceUnavailable:         "server-error-service-unavailable",
	StatusErrorVersionNotSupported:        "server-error-version-not-supported",
	StatusErrorDevice:                     "server-error-device-error",
	StatusErrorTemporary:                  "server-error-temporary-error",
	StatusErrorNotAcceptingJobs:           "server-error-not-accepting-jobs",
	StatusErrorBusy:                       "server-error-busy",
	StatusErrorJobCanceled:                "server-error-job-canceled",
	StatusErrorMultipleJobsNotSupported:   "server-error-multiple-document-jobs-not-supported",
	StatusErrorPrinterIsDeactivated:       "server-error-printer-is-deactivated",
	StatusErrorTooManyJobs:                "server-error-too-many-jobs",
	StatusErrorTooManyDocuments:           "server-error-too-many-documents",
}
07070100000017000081A400000000000000000000000167F56AFB000004C3000000000000000000000000000000000000001B00000000goipp-1.2.0/status_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Status Codes tests
 */

package goipp

import "testing"

// TestStatusString tests Status.String method
func TestStatusString(t *testing.T) {
	type testData struct {
		status Status // Input Op code
		s      string // Expected output string
	}

	tests := []testData{
		{StatusOk, "successful-ok"},
		{StatusOkConflicting, "successful-ok-conflicting-attributes"},
		{StatusOkEventsComplete, "successful-ok-events-complete"},
		{StatusRedirectionOtherSite, "redirection-other-site"},
		{StatusErrorBadRequest, "client-error-bad-request"},
		{StatusErrorForbidden, "client-error-forbidden"},
		{StatusErrorNotFetchable, "client-error-not-fetchable"},
		{StatusErrorInternal, "server-error-internal-error"},
		{StatusErrorTooManyDocuments, "server-error-too-many-documents"},
		{0xabcd, "0xabcd"},
	}

	for _, test := range tests {
		s := test.status.String()
		if s != test.s {
			t.Errorf("testing Status.String:\n"+
				"input:    0x%4.4x\n"+
				"expected: %s\n"+
				"present:  %s\n",
				int(test.status), test.s, s,
			)
		}
	}
}
07070100000018000081A400000000000000000000000167F56AFB00001CA1000000000000000000000000000000000000001300000000goipp-1.2.0/tag.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Tags
 */

package goipp

import (
	"fmt"
)

// Tag represents a tag used in the binary representation of an IPP message.
//
// Normally, a Tag is a single-byte value ranging from 0x00 to 0xff.
//
// However, IPP also provides an extension tag mechanism that supports
// 32-bit tags. When using this mechanism, the tag is set to [TagExtension],
// and the actual 32-bit tag value is encoded as a big-endian integer
// in the attribute value.
//
// This mechanism is described in RFC 8010, Section 3.5.2.
//
// To send an [Attribute] with an extended tag value:
//
//   - Set the value tag to TagExtension
//   - Use [Binary] to represent the Attribute value
//   - Encode the extended tag value as a big-endian integer in the first
//     4 bytes of the attribute value
//   - Note that the extended tag value must be within the allowed range
//     (0x00000000 to 0x7fffffff, inclusive)
//
// The goipp library enforces these rules during message encoding and decoding.
//
// Note: This API has changed since version 1.1.0 of this library.
// Previously, the library automatically converted tags with values exceeding
// single-byte range into extended tag representation. However, this caused
// issues during reception because automatically converted tags with extended
// representation format but smaller values became indistinguishable from
// normal tags with the same value, despite requiring different handling.
type Tag int

// Tag values
const (
	// Delimiter tags
	TagZero                   Tag = 0x00 // Zero tag - used for separators
	TagOperationGroup         Tag = 0x01 // Operation group
	TagJobGroup               Tag = 0x02 // Job group
	TagEnd                    Tag = 0x03 // End-of-attributes
	TagPrinterGroup           Tag = 0x04 // Printer group
	TagUnsupportedGroup       Tag = 0x05 // Unsupported attributes group
	TagSubscriptionGroup      Tag = 0x06 // Subscription group
	TagEventNotificationGroup Tag = 0x07 // Event group
	TagResourceGroup          Tag = 0x08 // Resource group
	TagDocumentGroup          Tag = 0x09 // Document group
	TagSystemGroup            Tag = 0x0a // System group
	TagFuture11Group          Tag = 0x0b // Future group 11
	TagFuture12Group          Tag = 0x0c // Future group 12
	TagFuture13Group          Tag = 0x0d // Future group 13
	TagFuture14Group          Tag = 0x0e // Future group 14
	TagFuture15Group          Tag = 0x0f // Future group 15

	// Value tags
	TagUnsupportedValue Tag = 0x10 // Unsupported value
	TagDefault          Tag = 0x11 // Default value
	TagUnknown          Tag = 0x12 // Unknown value
	TagNoValue          Tag = 0x13 // No-value value
	TagNotSettable      Tag = 0x15 // Not-settable value
	TagDeleteAttr       Tag = 0x16 // Delete-attribute value
	TagAdminDefine      Tag = 0x17 // Admin-defined value
	TagInteger          Tag = 0x21 // Integer value
	TagBoolean          Tag = 0x22 // Boolean value
	TagEnum             Tag = 0x23 // Enumeration value
	TagString           Tag = 0x30 // Octet string value
	TagDateTime         Tag = 0x31 // Date/time value
	TagResolution       Tag = 0x32 // Resolution value
	TagRange            Tag = 0x33 // Range value
	TagBeginCollection  Tag = 0x34 // Beginning of collection value
	TagTextLang         Tag = 0x35 // Text-with-language value
	TagNameLang         Tag = 0x36 // Name-with-language value
	TagEndCollection    Tag = 0x37 // End of collection value
	TagText             Tag = 0x41 // Text value
	TagName             Tag = 0x42 // Name value
	TagReservedString   Tag = 0x43 // Reserved for future string value
	TagKeyword          Tag = 0x44 // Keyword value
	TagURI              Tag = 0x45 // URI value
	TagURIScheme        Tag = 0x46 // URI scheme value
	TagCharset          Tag = 0x47 // Character set value
	TagLanguage         Tag = 0x48 // Language value
	TagMimeType         Tag = 0x49 // MIME media type value
	TagMemberName       Tag = 0x4a // Collection member name value
	TagExtension        Tag = 0x7f // Extension point for 32-bit tags
)

// IsDelimiter returns true for delimiter tags
func (tag Tag) IsDelimiter() bool {
	return uint(tag) < 0x10
}

// IsGroup returns true for group tags
func (tag Tag) IsGroup() bool {
	return tag.IsDelimiter() && tag != TagZero && tag != TagEnd
}

// Type returns Type of Value that corresponds to the tag
func (tag Tag) Type() Type {
	if tag.IsDelimiter() {
		return TypeInvalid
	}

	switch tag {
	case TagInteger, TagEnum:
		return TypeInteger

	case TagBoolean:
		return TypeBoolean

	case TagUnsupportedValue, TagDefault, TagUnknown, TagNotSettable,
		TagNoValue, TagDeleteAttr, TagAdminDefine:
		// These tags not expected to have value
		return TypeVoid

	case TagText, TagName, TagReservedString, TagKeyword, TagURI, TagURIScheme,
		TagCharset, TagLanguage, TagMimeType, TagMemberName:
		return TypeString

	case TagDateTime:
		return TypeDateTime

	case TagResolution:
		return TypeResolution

	case TagRange:
		return TypeRange

	case TagTextLang, TagNameLang:
		return TypeTextWithLang

	case TagBeginCollection:
		return TypeCollection

	case TagEndCollection:
		return TypeVoid

	case TagExtension:
		return TypeBinary

	default:
		return TypeBinary
	}
}

// String() returns a tag name, as defined by RFC 8010
func (tag Tag) String() string {
	if 0 <= tag && int(tag) < len(tagNames) {
		if s := tagNames[tag]; s != "" {
			return s
		}
	}

	if tag < 0x100 {
		return fmt.Sprintf("0x%2.2x", uint32(tag))
	}

	return fmt.Sprintf("0x%8.8x", uint(tag))
}

var tagNames = [...]string{
	// Delimiter tags
	TagZero:                   "zero",
	TagOperationGroup:         "operation-attributes-tag",
	TagJobGroup:               "job-attributes-tag",
	TagEnd:                    "end-of-attributes-tag",
	TagPrinterGroup:           "printer-attributes-tag",
	TagUnsupportedGroup:       "unsupported-attributes-tag",
	TagSubscriptionGroup:      "subscription-attributes-tag",
	TagEventNotificationGroup: "event-notification-attributes-tag",
	TagResourceGroup:          "resource-attributes-tag",
	TagDocumentGroup:          "document-attributes-tag",
	TagSystemGroup:            "system-attributes-tag",

	// Value tags
	TagUnsupportedValue: "unsupported",
	TagDefault:          "default",
	TagUnknown:          "unknown",
	TagNoValue:          "no-value",
	TagNotSettable:      "not-settable",
	TagDeleteAttr:       "delete-attribute",
	TagAdminDefine:      "admin-define",
	TagInteger:          "integer",
	TagBoolean:          "boolean",
	TagEnum:             "enum",
	TagString:           "octetString",
	TagDateTime:         "dateTime",
	TagResolution:       "resolution",
	TagRange:            "rangeOfInteger",
	TagBeginCollection:  "collection",
	TagTextLang:         "textWithLanguage",
	TagNameLang:         "nameWithLanguage",
	TagEndCollection:    "endCollection",
	TagText:             "textWithoutLanguage",
	TagName:             "nameWithoutLanguage",
	TagKeyword:          "keyword",
	TagURI:              "uri",
	TagURIScheme:        "uriScheme",
	TagCharset:          "charset",
	TagLanguage:         "naturalLanguage",
	TagMimeType:         "mimeMediaType",
	TagMemberName:       "memberAttrName",
	TagExtension:        "extension",
}
07070100000019000081A400000000000000000000000167F56AFB00000E72000000000000000000000000000000000000001800000000goipp-1.2.0/tag_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * IPP Tags tests
 */

package goipp

import "testing"

// TestTagIsDelimiter tests Tag.IsDelimiter function
func TestTagIsDelimiter(t *testing.T) {
	type testData struct {
		t      Tag
		answer bool
	}

	tests := []testData{
		{TagZero, true},
		{TagOperationGroup, true},
		{TagJobGroup, true},
		{TagEnd, true},
		{TagFuture15Group, true},
		{TagUnsupportedValue, false},
		{TagUnknown, false},
		{TagInteger, false},
		{TagBeginCollection, false},
		{TagEndCollection, false},
		{TagExtension, false},
	}

	for _, test := range tests {
		answer := test.t.IsDelimiter()
		if answer != test.answer {
			t.Errorf("testing Tag.IsDelimiter:\n"+
				"tag:      %s (0x%.2x)\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.t, uint32(test.t), test.answer, answer,
			)
		}
	}
}

// TestTagIsGroup tests Tag.IsGroup function
func TestTagIsGroup(t *testing.T) {
	type testData struct {
		t      Tag
		answer bool
	}

	tests := []testData{
		{TagZero, false},
		{TagOperationGroup, true},
		{TagJobGroup, true},
		{TagEnd, false},
		{TagPrinterGroup, true},
		{TagUnsupportedGroup, true},
		{TagSubscriptionGroup, true},
		{TagEventNotificationGroup, true},
		{TagResourceGroup, true},
		{TagDocumentGroup, true},
		{TagSystemGroup, true},
		{TagFuture11Group, true},
		{TagFuture12Group, true},
		{TagFuture13Group, true},
		{TagFuture14Group, true},
		{TagFuture15Group, true},
		{TagInteger, false},
	}

	for _, test := range tests {
		answer := test.t.IsGroup()
		if answer != test.answer {
			t.Errorf("testing Tag.IsGroup:\n"+
				"tag:      %s (0x%.2x)\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.t, uint32(test.t), test.answer, answer,
			)
		}
	}
}

// TestTagType tests Tag.Type function
func TestTagType(t *testing.T) {
	type testData struct {
		t      Tag
		answer Type
	}

	tests := []testData{
		{TagZero, TypeInvalid},
		{TagInteger, TypeInteger},
		{TagEnum, TypeInteger},
		{TagBoolean, TypeBoolean},
		{TagUnsupportedValue, TypeVoid},
		{TagDefault, TypeVoid},
		{TagUnknown, TypeVoid},
		{TagNotSettable, TypeVoid},
		{TagNoValue, TypeVoid},
		{TagDeleteAttr, TypeVoid},
		{TagAdminDefine, TypeVoid},
		{TagText, TypeString},
		{TagName, TypeString},
		{TagReservedString, TypeString},
		{TagKeyword, TypeString},
		{TagURI, TypeString},
		{TagURIScheme, TypeString},
		{TagCharset, TypeString},
		{TagLanguage, TypeString},
		{TagMimeType, TypeString},
		{TagMemberName, TypeString},
		{TagDateTime, TypeDateTime},
		{TagResolution, TypeResolution},
		{TagRange, TypeRange},
		{TagTextLang, TypeTextWithLang},
		{TagNameLang, TypeTextWithLang},
		{TagBeginCollection, TypeCollection},
		{TagEndCollection, TypeVoid},
		{TagExtension, TypeBinary},
		{0x1234, TypeBinary},
	}

	for _, test := range tests {
		answer := test.t.Type()
		if answer != test.answer {
			t.Errorf("testing Tag.Type:\n"+
				"tag:      %s (0x%.2x)\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.t, uint32(test.t), test.answer, answer,
			)
		}
	}
}

// TestTagString tests Tag.String function
func TestTagString(t *testing.T) {
	type testData struct {
		t      Tag
		answer string
	}

	tests := []testData{
		{TagZero, "zero"},
		{TagUnsupportedValue, "unsupported"},
		{-1, "0xffffffff"},
		{0xff, "0xff"},
		{0x1234, "0x00001234"},
	}

	for _, test := range tests {
		answer := test.t.String()
		if answer != test.answer {
			t.Errorf("testing Tag.IsGroup:\n"+
				"tag:      %s (0x%.2x)\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.t, uint32(test.t), test.answer, answer,
			)
		}
	}
}
0707010000001A000081A400000000000000000000000167F56AFB000005CE000000000000000000000000000000000000001400000000goipp-1.2.0/type.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Enumeration of value types
 */

package goipp

import (
	"fmt"
)

// Type enumerates all possible value types
type Type int

// Type values
const (
	TypeInvalid      Type = -1   // Invalid Value type
	TypeVoid         Type = iota // Value is Void
	TypeInteger                  // Value is Integer
	TypeBoolean                  // Value is Boolean
	TypeString                   // Value is String
	TypeDateTime                 // Value is Time
	TypeResolution               // Value is Resolution
	TypeRange                    // Value is Range
	TypeTextWithLang             // Value is TextWithLang
	TypeBinary                   // Value is Binary
	TypeCollection               // Value is Collection
)

// String converts Type to string, for debugging
func (t Type) String() string {
	if t == TypeInvalid {
		return "Invalid"
	}

	if 0 <= t && int(t) < len(typeNames) {
		if s := typeNames[t]; s != "" {
			return s
		}
	}

	return fmt.Sprintf("0x%4.4x", uint(t))
}

var typeNames = [...]string{
	TypeVoid:         "Void",
	TypeInteger:      "Integer",
	TypeBoolean:      "Boolean",
	TypeString:       "String",
	TypeDateTime:     "DateTime",
	TypeResolution:   "Resolution",
	TypeRange:        "Range",
	TypeTextWithLang: "TextWithLang",
	TypeBinary:       "Binary",
	TypeCollection:   "Collection",
}
0707010000001B000081A400000000000000000000000167F56AFB000003DC000000000000000000000000000000000000001900000000goipp-1.2.0/type_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Tests for enumeration of value types
 */

package goipp

import "testing"

// TestTypeString tests Type.String function
func TestTypeString(t *testing.T) {
	type testData struct {
		ty Type   // Input Type
		s  string // Expected output string
	}

	tests := []testData{
		{TypeInvalid, "Invalid"},
		{TypeVoid, "Void"},
		{TypeBoolean, "Boolean"},
		{TypeString, "String"},
		{TypeDateTime, "DateTime"},
		{TypeResolution, "Resolution"},
		{TypeRange, "Range"},
		{TypeTextWithLang, "TextWithLang"},
		{TypeBinary, "Binary"},
		{TypeCollection, "Collection"},
		{0x1234, "0x1234"},
	}

	for _, test := range tests {
		s := test.ty.String()
		if s != test.s {
			t.Errorf("testing Type.String:\n"+
				"input:    %d\n"+
				"expected: %s\n"+
				"present:  %s\n",
				int(test.ty), test.s, s,
			)
		}
	}
}
0707010000001C000081A400000000000000000000000167F56AFB0000474A000000000000000000000000000000000000001500000000goipp-1.2.0/value.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Values for message attributes
 */

package goipp

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"time"
)

// Values represents a sequence of values with tags.
// Usually Values used as a "payload" of Attribute
type Values []struct {
	T Tag   // The tag
	V Value // The value
}

// Add Value to Values
func (values *Values) Add(t Tag, v Value) {
	*values = append(*values, struct {
		T Tag
		V Value
	}{t, v})
}

// String converts Values to string
func (values Values) String() string {
	if len(values) == 1 {
		return values[0].V.String()
	}

	var buf bytes.Buffer
	buf.Write([]byte("["))
	for i, v := range values {
		if i != 0 {
			buf.Write([]byte(","))
		}
		buf.Write([]byte(v.V.String()))
	}
	buf.Write([]byte("]"))

	return buf.String()
}

// Clone creates a shallow copy of Values.
// For nil input it returns nil output.
func (values Values) Clone() Values {
	var values2 Values
	if values != nil {
		values2 = make(Values, len(values))
		copy(values2, values)
	}
	return values2
}

// DeepCopy creates a deep copy of Values
// For nil input it returns nil output.
func (values Values) DeepCopy() Values {
	var values2 Values
	if values != nil {
		values2 = make(Values, len(values))
		for i := range values {
			values2[i].T = values[i].T
			values2[i].V = values[i].V.DeepCopy()
		}
	}
	return values2
}

// Equal performs deep check of equality of two Values.
//
// Note, Values(nil) and Values{} are not Equal but Similar.
func (values Values) Equal(values2 Values) bool {
	if len(values) != len(values2) {
		return false
	}

	if (values == nil) != (values2 == nil) {
		return false
	}

	for i, v := range values {
		v2 := values2[i]
		if v.T != v2.T || !ValueEqual(v.V, v2.V) {
			return false
		}
	}

	return true
}

// Similar performs deep check of **logical** equality of two Values
//
// Note, Values(nil) and Values{} are not Equal but Similar.
func (values Values) Similar(values2 Values) bool {
	if len(values) != len(values2) {
		return false
	}

	for i, v := range values {
		v2 := values2[i]
		if v.T != v2.T || !ValueSimilar(v.V, v2.V) {
			return false
		}
	}

	return true
}

// Value represents an attribute value
//
// IPP uses typed values, and type of each value is unambiguously
// defined by the attribute tag
type Value interface {
	String() string
	Type() Type
	DeepCopy() Value
	encode() ([]byte, error)
	decode([]byte) (Value, error)
}

var (
	_ = Value(Binary(nil))
	_ = Value(Boolean(false))
	_ = Value(Collection(nil))
	_ = Value(Integer(0))
	_ = Value(Range{})
	_ = Value(Resolution{})
	_ = Value(String(""))
	_ = Value(TextWithLang{})
	_ = Value(Time{time.Time{}})
	_ = Value(Void{})
)

// IntegerOrRange is a Value of type Integer or Range
type IntegerOrRange interface {
	Value

	// Within checks that x fits within the range:
	//
	//   for Integer: x == Integer's value
	//   for Range:   Lower <= x && x <= Upper
	Within(x int) bool
}

var (
	_ = IntegerOrRange(Integer(0))
	_ = IntegerOrRange(Range{})
)

// ValueEqual checks if two values are equal
//
// Equality means that types and values are equal. For structured
// values, like Collection, deep comparison is performed
func ValueEqual(v1, v2 Value) bool {
	if v1.Type() != v2.Type() {
		return false
	}

	switch v1.Type() {
	case TypeDateTime:
		return v1.(Time).Equal(v2.(Time).Time)
	case TypeBinary:
		return bytes.Equal(v1.(Binary), v2.(Binary))
	case TypeCollection:
		c1 := Attributes(v1.(Collection))
		c2 := Attributes(v2.(Collection))
		return c1.Equal(c2)
	}

	return v1 == v2
}

// ValueSimilar checks if two values are **logically** equal,
// which means the following:
//   - If values are equal (i.e., ValueEqual() returns true),
//     they are similar.
//   - Binary and String values are similar, if they represent
//     the same sequence of bytes.
//   - Two collections are similar, if they contain the same
//     set of attributes (but may be differently ordered) and
//     values of these attributes are similar.
func ValueSimilar(v1, v2 Value) bool {
	if ValueEqual(v1, v2) {
		return true
	}

	t1 := v1.Type()
	t2 := v2.Type()

	switch {
	case t1 == TypeBinary && t2 == TypeString:
		return bytes.Equal(v1.(Binary), []byte(v2.(String)))

	case t1 == TypeString && t2 == TypeBinary:
		return bytes.Equal([]byte(v1.(String)), v2.(Binary))

	case t1 == TypeCollection && t2 == TypeCollection:
		return Attributes(v1.(Collection)).Similar(
			Attributes(v2.(Collection)))
	}

	return false
}

// Void is the Value that represents "no value"
//
// Use with: TagUnsupportedValue, TagDefault, TagUnknown,
// TagNotSettable, TagDeleteAttr, TagAdminDefine
type Void struct{}

// String converts Void Value to string
func (Void) String() string { return "" }

// Type returns type of Value (TypeVoid for Void)
func (Void) Type() Type { return TypeVoid }

// DeepCopy returns a deep copy of the Void Value
func (v Void) DeepCopy() Value {
	return v
}

// Encode Void Value into wire format
func (v Void) encode() ([]byte, error) {
	return []byte{}, nil
}

// Decode Void Value from wire format
func (Void) decode([]byte) (Value, error) {
	return Void{}, nil
}

// Integer is the Value that represents 32-bit signed int
//
// Use with: TagInteger, TagEnum
type Integer int32

// String converts Integer value to string
func (v Integer) String() string { return fmt.Sprintf("%d", int32(v)) }

// Type returns type of Value (TypeInteger for Integer)
func (Integer) Type() Type { return TypeInteger }

// DeepCopy returns a deep copy of the Integer Value
func (v Integer) DeepCopy() Value {
	return v
}

// Within checks that x fits within the range
//
// It implements IntegerOrRange interface
func (v Integer) Within(x int) bool {
	return x == int(v)
}

// Encode Integer Value into wire format
func (v Integer) encode() ([]byte, error) {
	return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil
}

// Decode Integer Value from wire format
func (Integer) decode(data []byte) (Value, error) {
	if len(data) != 4 {
		return nil, errors.New("value must be 4 bytes")
	}

	return Integer(int32(binary.BigEndian.Uint32(data))), nil
}

// Boolean is the Value that contains true of false
//
// Use with: TagBoolean
type Boolean bool

// String converts Boolean value to string
func (v Boolean) String() string { return fmt.Sprintf("%t", bool(v)) }

// Type returns type of Value (TypeBoolean for Boolean)
func (Boolean) Type() Type { return TypeBoolean }

// DeepCopy returns a deep copy of the Boolean Value
func (v Boolean) DeepCopy() Value {
	return v
}

// Encode Boolean Value into wire format
func (v Boolean) encode() ([]byte, error) {
	if v {
		return []byte{1}, nil
	}
	return []byte{0}, nil
}

// Decode Boolean Value from wire format
func (Boolean) decode(data []byte) (Value, error) {
	if len(data) != 1 {
		return nil, errors.New("value must be 1 byte")
	}

	return Boolean(data[0] != 0), nil
}

// String is the Value that represents string of text
//
// Use with: TagText, TagName, TagReservedString, TagKeyword, TagURI,
// TagURIScheme, TagCharset, TagLanguage, TagMimeType, TagMemberName
type String string

// String converts String value to string
func (v String) String() string { return string(v) }

// Type returns type of Value (TypeString for String)
func (String) Type() Type { return TypeString }

// DeepCopy returns a deep copy of the String Value
func (v String) DeepCopy() Value {
	return v
}

// Encode String Value into wire format
func (v String) encode() ([]byte, error) {
	return []byte(v), nil
}

// Decode String Value from wire format
func (String) decode(data []byte) (Value, error) {
	return String(data), nil
}

// Time is the Value that represents DataTime
//
// Use with: TagTime
type Time struct{ time.Time }

// String converts Time value to string
func (v Time) String() string { return v.Time.Format(time.RFC3339) }

// Type returns type of Value (TypeDateTime for Time)
func (Time) Type() Type { return TypeDateTime }

// DeepCopy returns a deep copy of the Time Value
func (v Time) DeepCopy() Value {
	return v
}

// Encode Time Value into wire format
func (v Time) encode() ([]byte, error) {
	// From RFC2579:
	//
	//     field  octets  contents                  range
	//     -----  ------  --------                  -----
	//       1      1-2   year*                     0..65536
	//       2       3    month                     1..12
	//       3       4    day                       1..31
	//       4       5    hour                      0..23
	//       5       6    minutes                   0..59
	//       6       7    seconds                   0..60
	//                    (use 60 for leap-second)
	//       7       8    deci-seconds              0..9
	//       8       9    direction from UTC        '+' / '-'
	//       9      10    hours from UTC*           0..13
	//      10      11    minutes from UTC          0..59
	//
	//     * Notes:
	//     - the value of year is in network-byte order
	//     - daylight saving time in New Zealand is +13

	year := v.Year()
	_, zone := v.Zone()
	dir := byte('+')
	if zone < 0 {
		zone = -zone
		dir = '-'
	}

	return []byte{
		byte(year >> 8), byte(year),
		byte(v.Month()),
		byte(v.Day()),
		byte(v.Hour()),
		byte(v.Minute()),
		byte(v.Second()),
		byte(v.Nanosecond() / 100000000),
		dir,
		byte(zone / 3600),
		byte((zone / 60) % 60),
	}, nil
}

// Decode Time Value from wire format
func (Time) decode(data []byte) (Value, error) {
	// Check size
	if len(data) != 11 {
		return nil, errors.New("value must be 11 bytes")
	}

	// Validate ranges
	var err error
	switch {
	case data[2] < 1 || data[2] > 12:
		err = fmt.Errorf("bad month %d", data[2])
	case data[3] < 1 || data[3] > 31:
		err = fmt.Errorf("bad day %d", data[3])
	case data[4] > 23:
		err = fmt.Errorf("bad hours %d", data[4])
	case data[5] > 59:
		err = fmt.Errorf("bad minutes %d", data[5])
	case data[6] > 60:
		err = fmt.Errorf("bad seconds %d", data[6])
	case data[7] > 9:
		err = fmt.Errorf("bad deciseconds %d", data[7])
	case data[8] != '+' && data[8] != '-':
		return nil, errors.New("bad UTC sign")
	case data[9] > 11:
		err = fmt.Errorf("bad UTC hours %d", data[9])
	case data[10] > 59:
		err = fmt.Errorf("bad UTC minutes %d", data[10])
	}

	if err != nil {
		return Time{}, err
	}

	// Decode time zone
	tzName := fmt.Sprintf("UTC%c%d", data[8], data[9])
	if data[10] != 0 {
		tzName += fmt.Sprintf(":%d", data[10])
	}

	tzOff := 3600*int(data[9]) + 60*int(data[10])
	if data[8] == '-' {
		tzOff = -tzOff
	}

	tz := time.FixedZone(tzName, tzOff)

	// Decode time
	t := time.Date(
		int(binary.BigEndian.Uint16(data[0:2])), // year
		time.Month(data[2]),                     // month
		int(data[3]),                            // day
		int(data[4]),                            // hour
		int(data[5]),                            // min
		int(data[6]),                            // sec
		int(data[7])*100000000,                  // nsec
		tz,                                      // time zone
	)

	return Time{t}, nil
}

// Resolution is the Value that represents image resolution.
//
// Use with: TagResolution
type Resolution struct {
	Xres, Yres int   // X/Y resolutions
	Units      Units // Resolution units
}

// String converts Resolution value to string
func (v Resolution) String() string {
	return fmt.Sprintf("%dx%d%s", v.Xres, v.Yres, v.Units)
}

// Type returns type of Value (TypeResolution for Resolution)
func (Resolution) Type() Type { return TypeResolution }

// DeepCopy returns a deep copy of the Resolution Value
func (v Resolution) DeepCopy() Value {
	return v
}

// Encode Resolution Value into wire format
func (v Resolution) encode() ([]byte, error) {
	// Wire format
	//    4 bytes: Xres
	//    4 bytes: Yres
	//    1 byte:  Units

	x, y := v.Xres, v.Yres

	return []byte{
		byte(x >> 24), byte(x >> 16), byte(x >> 8), byte(x),
		byte(y >> 24), byte(y >> 16), byte(y >> 8), byte(y),
		byte(v.Units),
	}, nil
}

// Decode Resolution Value from wire format
func (Resolution) decode(data []byte) (Value, error) {
	if len(data) != 9 {
		return nil, errors.New("value must be 9 bytes")
	}

	return Resolution{
		Xres:  int(int32(binary.BigEndian.Uint32(data[0:4]))),
		Yres:  int(int32(binary.BigEndian.Uint32(data[4:8]))),
		Units: Units(data[8]),
	}, nil

}

// Units represents resolution units
type Units uint8

// Resolution units codes
const (
	UnitsDpi  Units = 3 // Dots per inch
	UnitsDpcm Units = 4 // Dots per cm
)

// String converts Units to string
func (u Units) String() string {
	switch u {
	case UnitsDpi:
		return "dpi"
	case UnitsDpcm:
		return "dpcm"
	default:
		return fmt.Sprintf("unknown(0x%2.2x)", uint8(u))
	}
}

// Range is the Value that represents a range of 32-bit signed integers
//
// Use with: TagRange
type Range struct {
	Lower, Upper int // Lower/upper bounds
}

// String converts Range value to string
func (v Range) String() string {
	return fmt.Sprintf("%d-%d", v.Lower, v.Upper)
}

// Type returns type of Value (TypeRange for Range)
func (Range) Type() Type { return TypeRange }

// DeepCopy returns a deep copy of the Range Value
func (v Range) DeepCopy() Value {
	return v
}

// Encode Range Value into wire format
func (v Range) encode() ([]byte, error) {
	// Wire format
	//    4 bytes: Lower
	//    4 bytes: Upper

	l, u := v.Lower, v.Upper

	return []byte{
		byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l),
		byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u),
	}, nil
}

// Within checks that x fits within the range
//
// It implements IntegerOrRange interface
func (v Range) Within(x int) bool {
	return v.Lower <= x && x <= v.Upper
}

// Decode Range Value from wire format
func (Range) decode(data []byte) (Value, error) {
	if len(data) != 8 {
		return nil, errors.New("value must be 8 bytes")
	}

	return Range{
		Lower: int(int32(binary.BigEndian.Uint32(data[0:4]))),
		Upper: int(int32(binary.BigEndian.Uint32(data[4:8]))),
	}, nil
}

// TextWithLang is the Value that represents a combination
// of two strings:
//   - text on some natural language (i.e., "hello")
//   - name of that language (i.e., "en")
//
// Use with: TagTextLang, TagNameLang
type TextWithLang struct {
	Lang, Text string // Language and text
}

// String converts TextWithLang value to string
func (v TextWithLang) String() string { return v.Text + " [" + v.Lang + "]" }

// Type returns type of Value (TypeTextWithLang for TextWithLang)
func (TextWithLang) Type() Type { return TypeTextWithLang }

// DeepCopy returns a deep copy of the TextWithLang Value
func (v TextWithLang) DeepCopy() Value {
	return v
}

// Encode TextWithLang Value into wire format
func (v TextWithLang) encode() ([]byte, error) {
	// Wire format
	//    2 bytes:  len(Lang)
	//    variable: Lang
	//    2 bytes:  len(Text)
	//    variable: Text

	lang := []byte(v.Lang)
	text := []byte(v.Text)

	if len(lang) > math.MaxUint16 {
		return nil, fmt.Errorf("Lang exceeds %d bytes", math.MaxUint16)
	}

	if len(text) > math.MaxUint16 {
		return nil, fmt.Errorf("Text exceeds %d bytes", math.MaxUint16)
	}

	data := make([]byte, 2+2+len(lang)+len(text))
	binary.BigEndian.PutUint16(data, uint16(len(lang)))
	copy(data[2:], []byte(lang))

	data2 := data[2+len(lang):]
	binary.BigEndian.PutUint16(data2, uint16(len(text)))
	copy(data2[2:], []byte(text))

	return data, nil
}

// Decode TextWithLang Value from wire format
func (TextWithLang) decode(data []byte) (Value, error) {
	var langLen, textLen int
	var lang, text string

	// Unpack language length
	if len(data) < 2 {
		return nil, errors.New("truncated language length")
	}

	langLen = int(binary.BigEndian.Uint16(data[0:2]))
	data = data[2:]

	// Unpack language value
	if len(data) < langLen {
		return nil, errors.New("truncated language name")
	}

	lang = string(data[:langLen])
	data = data[langLen:]

	// Unpack text length
	if len(data) < 2 {
		return nil, errors.New("truncated text length")
	}

	textLen = int(binary.BigEndian.Uint16(data[0:2]))
	data = data[2:]

	// Unpack text value
	if len(data) < textLen {
		return nil, errors.New("truncated text string")
	}

	text = string(data[:textLen])
	data = data[textLen:]

	// We must have consumed all bytes at this point
	if len(data) != 0 {
		return nil, fmt.Errorf("extra %d bytes at the end of value",
			len(data))
	}

	// Return a value
	return TextWithLang{Lang: lang, Text: text}, nil
}

// Binary is the Value that represents a raw binary data
type Binary []byte

// String converts Binary value to string
func (v Binary) String() string {
	return fmt.Sprintf("%x", []byte(v))
}

// Type returns type of Value (TypeBinary for Binary)
func (Binary) Type() Type { return TypeBinary }

// DeepCopy returns a deep copy of the Binary Value
func (v Binary) DeepCopy() Value {
	v2 := make(Binary, len(v))
	copy(v2, v)
	return v2
}

// Encode TextWithLang Value into wire format
func (v Binary) encode() ([]byte, error) {
	return []byte(v), nil
}

// Decode Binary Value from wire format
func (Binary) decode(data []byte) (Value, error) {
	return Binary(data), nil
}

// Collection is the Value that represents collection of attributes
//
// Use with: TagBeginCollection
type Collection Attributes

// Add Attribute to Attributes
func (v *Collection) Add(attr Attribute) {
	*v = append(*v, attr)
}

// String converts Collection to string
func (v Collection) String() string {
	var buf bytes.Buffer
	buf.Write([]byte("{"))
	for i, attr := range v {
		if i > 0 {
			buf.Write([]byte(" "))
		}
		fmt.Fprintf(&buf, "%s=%s", attr.Name, attr.Values)
	}
	buf.Write([]byte("}"))

	return buf.String()
}

// Type returns type of Value (TypeCollection for Collection)
func (Collection) Type() Type { return TypeCollection }

// DeepCopy returns a deep copy of the Collection Value
func (v Collection) DeepCopy() Value {
	return Collection(Attributes(v).DeepCopy())
}

// Encode Collection Value into wire format
func (Collection) encode() ([]byte, error) {
	// Note, TagBeginCollection attribute contains
	// no data, collection itself handled the different way
	return []byte{}, nil
}

// Decode Collection Value from wire format
func (Collection) decode(data []byte) (Value, error) {
	panic("internal error")
}
0707010000001D000081A400000000000000000000000167F56AFB000058F8000000000000000000000000000000000000001A00000000goipp-1.2.0/value_test.go/* Go IPP - IPP core protocol implementation in pure Go
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Values test
 */

package goipp

import (
	"bytes"
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"
)

// TestValueEncode tests Value.encode for all value types
func TestValueEncode(t *testing.T) {
	noError := errors.New("")
	longstr := strings.Repeat("x", 65536)
	loc1 := time.FixedZone("UTC+3:30", 3*3600+1800)
	tm1, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53+03:30", loc1)
	loc2 := time.FixedZone("UTC-3", -3*3600)
	tm2, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53-03:00", loc2)

	type testData struct {
		v    Value  // Input value
		data []byte // Expected output data
		err  string // Expected error string ("" if no error)
	}

	tests := []testData{
		// Simple values
		{Binary{}, []byte{}, ""},
		{Binary{1, 2, 3}, []byte{1, 2, 3}, ""},
		{Boolean(false), []byte{0}, ""},
		{Boolean(true), []byte{1}, ""},
		{Integer(0), []byte{0, 0, 0, 0}, ""},
		{Integer(0x01020304), []byte{1, 2, 3, 4}, ""},
		{String(""), []byte{}, ""},
		{String("Hello"), []byte("Hello"), ""},
		{Void{}, []byte{}, ""},

		// Range
		{
			v:    Range{0x01020304, 0x05060708},
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		},
		{
			v: Range{-100, 100},
			data: []byte{
				0xff, 0xff, 0xff, 0x9c, 0x00, 0x00, 0x00, 0x64,
			},
		},

		// Resolution
		{
			v:    Resolution{0x01020304, 0x05060708, 0x09},
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
		},
		{
			v: Resolution{150, 300, UnitsDpi},
			data: []byte{
				0x00, 0x00, 0x00, 0x96, // 150
				0x00, 0x00, 0x01, 0x2c, // 300
				0x03,
			},
		},

		// TextWithLang
		{
			v: TextWithLang{"en-US", "Hello!"},
			data: []byte{
				0x00, 0x05,
				'e', 'n', '-', 'U', 'S',
				0x00, 0x06,
				'H', 'e', 'l', 'l', 'o', '!',
			},
		},

		{
			v: TextWithLang{"ru-RU", "Привет!"},
			data: []byte{
				0x00, 0x05,
				'r', 'u', '-', 'R', 'U',
				0x00, 0x0d,
				0xd0, 0x9f, 0xd1, 0x80, 0xd0, 0xb8, 0xd0, 0xb2,
				0xd0, 0xb5, 0xd1, 0x82, 0x21,
			},
		},

		{
			v:   TextWithLang{"en-US", longstr},
			err: "Text exceeds 65535 bytes",
		},

		{
			v:   TextWithLang{longstr, "hello"},
			err: "Lang exceeds 65535 bytes",
		},

		// Time
		{
			v: Time{tm1},
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x1e, // Minutes from UTC
			},
		},

		{
			v: Time{tm2},
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'-',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
		},

		// Collection
		//
		// Note, Collection.encode is the stub and encodes
		// as Void. Actual collection encoding handled the
		// different way.
		{
			v: Collection{
				MakeAttribute("test", TagString, String("")),
			},
			data: []byte{},
		},
	}

	for _, test := range tests {
		data, err := test.v.encode()
		if err == nil {
			err = noError
		}

		vstr := test.v.String()
		if len(vstr) > 40 {
			vstr = vstr[:40] + "..."
		}

		if err.Error() != test.err {
			t.Errorf("testing %s.encode:\n"+
				"value:          %s\n"+
				"error expected: %q\n"+
				"error present:  %q\n",
				reflect.TypeOf(test.v).String(),
				vstr,
				test.err,
				err,
			)
			continue
		}

		if test.err == "" && !bytes.Equal(data, test.data) {
			t.Errorf("testing %s.encode:\n"+
				"value:         %s\n"+
				"data expected: %x\n"+
				"data present:  %x\n",
				reflect.TypeOf(test.v).String(),
				vstr,
				test.data,
				data,
			)
		}
	}
}

// TestValueEncode tests Value.decode for all value types
func TestValueDecode(t *testing.T) {
	noError := errors.New("")
	loc1 := time.FixedZone("UTC+3:30", 3*3600+1800)
	tm1, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53+03:30", loc1)
	loc2 := time.FixedZone("UTC-3", -3*3600)
	tm2, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53-03:00", loc2)

	type testData struct {
		data []byte // Input data
		v    Value  // Expected output value
		err  string // Expected error string ("" if no error)
	}

	tests := []testData{
		// Simple types
		{[]byte{}, Binary{}, ""},
		{[]byte{1, 2, 3, 4}, Binary{1, 2, 3, 4}, ""},
		{[]byte{0}, Boolean(false), ""},
		{[]byte{1}, Boolean(true), ""},
		{[]byte{0, 1}, Boolean(false), "value must be 1 byte"},
		{[]byte{1, 2, 3, 4}, Integer(0x01020304), ""},
		{[]byte{0xff, 0xff, 0xff, 0xff}, Integer(-1), ""},
		{[]byte{}, Integer(0), "value must be 4 bytes"},
		{[]byte{1, 2, 3, 4, 5}, Integer(0), "value must be 4 bytes"},
		{[]byte{}, Void{}, ""},
		{[]byte("hello"), String("hello"), ""},
		{[]byte{1, 2, 3, 4, 5}, Void{}, ""},

		// Range
		{
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8},
			v:    Range{0x01020304, 0x05060708},
		},

		{
			data: []byte{
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
			},
			v: Range{-1, -1},
		},

		{
			data: []byte{1, 2, 3, 4, 5, 6, 7},
			v:    Range{},
			err:  "value must be 8 bytes",
		},

		{
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
			v:    Range{},
			err:  "value must be 8 bytes",
		},

		// Resolution
		{
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
			v:    Resolution{0x01020304, 0x05060708, 0x09},
		},

		{
			data: []byte{
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 9,
			},
			v: Resolution{-1, -1, 0x09},
		},

		{
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8},
			v:    Resolution{},
			err:  "value must be 9 bytes",
		},

		{
			data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			v:    Resolution{},
			err:  "value must be 9 bytes",
		},

		// Time
		{
			// Good time, UTC+...
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x1e, // Minutes from UTC
			},
			v: Time{tm1},
		},

		{
			// Good time, UTC-...
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'-',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v: Time{tm2},
		},

		{
			// Truncated data
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
			},
			v:   Time{},
			err: "value must be 11 bytes",
		},

		{
			// Extra data
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
				0,
			},
			v:   Time{},
			err: "value must be 11 bytes",
		},

		{
			// Bad month
			data: []byte{
				0x07, 0xe9,
				0,    // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad month 0",
		},

		{
			// Bad day
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				32,   // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad day 32",
		},

		{
			// Bad hours
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				99,   // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad hours 99",
		},

		{
			// Bad minutes
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				88,   // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad minutes 88",
		},

		{
			// Bad seconds
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				77,   // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad seconds 77",
		},

		{
			// Bad deciseconds
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				100,  // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad deciseconds 100",
		},

		{
			// Bad UTC sign
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'?',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad UTC sign",
		},

		{
			// Bad UTC hours
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				66,   // Hours from UTC
				0x00, // Minutes from UTC
			},
			v:   Time{},
			err: "bad UTC hours 66",
		},

		{
			// Bad UTC minutes
			data: []byte{
				0x07, 0xe9,
				0x03, // Month, 1...12
				0x1d, // Day, 1...31
				0x10, // Hour, 0...23
				0x30, // Minutes, 0...59
				0x35, // Seconds, 0...59
				0x00, // Deci-seconds, 0...9
				'+',  // Direction from UTC, +/-
				0x03, // Hours from UTC
				166,  // Minutes from UTC
			},
			v:   Time{},
			err: "bad UTC minutes 166",
		},

		// TextWithLang
		{
			data: []byte{
				0x00, 0x05,
				'e', 'n', '-', 'U', 'S',
				0x00, 0x06,
				'H', 'e', 'l', 'l', 'o', '!',
			},
			v: TextWithLang{"en-US", "Hello!"},
		},

		{
			data: []byte{
				0x00, 0x05,
				'r', 'u', '-', 'R', 'U',
				0x00, 0x0d,
				0xd0, 0x9f, 0xd1, 0x80, 0xd0, 0xb8, 0xd0, 0xb2,
				0xd0, 0xb5, 0xd1, 0x82, 0x21,
			},
			v: TextWithLang{"ru-RU", "Привет!"},
		},

		{
			// truncated language length
			data: []byte{},
			v:    TextWithLang{},
			err:  "truncated language length",
		},

		{
			// truncated language name
			data: []byte{
				0x00, 0x05,
				'e',
			},
			v:   TextWithLang{},
			err: "truncated language name",
		},

		{
			// truncated text length
			data: []byte{
				0x00, 0x05,
				'e', 'n', '-', 'U', 'S',
				0x00,
			},
			v:   TextWithLang{},
			err: "truncated text length",
		},

		{
			// truncated text string
			data: []byte{
				0x00, 0x05,
				'e', 'n', '-', 'U', 'S',
				0x00, 0x06,
				'H', 'e',
			},
			v:   TextWithLang{},
			err: "truncated text string",
		},

		{
			// extra data bytes
			data: []byte{
				0x00, 0x05,
				'e', 'n', '-', 'U', 'S',
				0x00, 0x06,
				'H', 'e', 'l', 'l', 'o', '!',
				0, 2, 3,
			},
			v:   TextWithLang{},
			err: "extra 3 bytes at the end of value",
		},
	}

	for _, test := range tests {
		v, err := test.v.decode(test.data)
		if err == nil {
			err = noError
		}

		if err.Error() != test.err {
			t.Errorf("testing %s.decode:\n"+
				"value:          %s\n"+
				"error expected: %q\n"+
				"error present:  %q\n",
				reflect.TypeOf(test.v).String(),
				v,
				test.err,
				err,
			)
			continue
		}

		if test.err == "" && !reflect.DeepEqual(v, test.v) {
			t.Errorf("testing %s.decode:\n"+
				"data:           %x\n"+
				"value expected: %#v\n"+
				"value present:  %#v\n",
				reflect.TypeOf(test.v).String(),
				test.data,
				test.v,
				v,
			)
		}

	}
}

// TestValueCollectionDecode tests Collection.decode for all value types
func TestValueCollectionDecode(t *testing.T) {
	// Collection.decode is a stub and must panic
	defer func() {
		recover()
	}()

	v := Collection{}
	v.decode([]byte{})

	t.Errorf("Collection.decode() method is a stub and must panic")
}

// TestValueString rests Value.String method for various
// kinds of the Value
func TestValueString(t *testing.T) {
	loc1 := time.FixedZone("UTC+3:30", 3*3600+1800)
	tm1, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53+03:30", loc1)

	type testData struct {
		v Value  // Input value
		s string // Expected output string
	}

	tests := []testData{
		// Simple types
		{Binary{}, ""},
		{Binary{1, 2, 3}, "010203"},
		{Integer(123), "123"},
		{Integer(-321), "-321"},
		{Range{-100, 200}, "-100-200"},
		{Range{-100, -50}, "-100--50"},
		{Resolution{150, 300, UnitsDpi}, "150x300dpi"},
		{Resolution{100, 200, UnitsDpcm}, "100x200dpcm"},
		{Resolution{75, 150, 10}, "75x150unknown(0x0a)"},
		{String("hello"), "hello"},
		{TextWithLang{"en-US", "hello"}, "hello [en-US]"},
		{Time{tm1}, "2025-03-29T16:48:53+03:30"},
		{Void{}, ""},

		// Collections
		{Collection{}, "{}"},

		{
			v: Collection{
				MakeAttr("attr1", TagInteger, Integer(1)),
				MakeAttr("attr2", TagString, String("hello")),
			},
			s: "{attr1=1 attr2=hello}",
		},
	}

	for _, test := range tests {
		s := test.v.String()
		if s != test.s {
			t.Errorf("testing %s.String:\n"+
				"value:    %#v\n"+
				"expected: %q\n"+
				"present:  %q\n",
				reflect.TypeOf(test.v).String(),
				test.v, test.s, s,
			)
		}
	}
}

// TestValueType rests Value.Type method for various
// kinds of the Value
func TestValueType(t *testing.T) {
	type testData struct {
		v  Value // Input value
		tp Type  // Expected output type
	}

	tests := []testData{
		{Binary(nil), TypeBinary},
		{Boolean(false), TypeBoolean},
		{Collection(nil), TypeCollection},
		{Integer(0), TypeInteger},
		{Range{}, TypeRange},
		{Resolution{}, TypeResolution},
		{String(""), TypeString},
		{TextWithLang{}, TypeTextWithLang},
		{Time{time.Time{}}, TypeDateTime},
		{Void{}, TypeVoid},
	}

	for _, test := range tests {
		tp := test.v.Type()
		if tp != test.tp {
			t.Errorf("testing %s.Type:\n"+
				"expected: %q\n"+
				"present:  %q\n",
				reflect.TypeOf(test.v).String(),
				test.tp, tp,
			)
		}
	}
}

// TestValueEqualSimilar tests ValueEqual and ValueSimilar
func TestValueEqualSimilar(t *testing.T) {
	tm1 := time.Now()
	tm2 := tm1.Add(time.Hour)

	type testData struct {
		v1, v2  Value // A pair of values
		equal   bool  // Expected ValueEqual(v1,v2) output
		similar bool  // Expected ValueSimilar(v1,v2) output
	}

	tests := []testData{
		// Simple types
		{Integer(0), Integer(0), true, true},
		{Integer(0), Integer(1), false, false},
		{Integer(0), String("hello"), false, false},
		{Time{tm1}, Time{tm1}, true, true},
		{Time{tm1}, Time{tm2}, false, false},
		{Binary{}, Binary{}, true, true},
		{Binary{}, Binary{1, 2, 3}, false, false},
		{Binary{1, 2, 3}, Binary{4, 5, 6}, false, false},
		{Binary("hello"), Binary("hello"), true, true},
		{String("hello"), String("hello"), true, true},
		{Binary("hello"), String("hello"), false, true},
		{String("hello"), Binary("hello"), false, true},

		// Collections
		//
		// Note, ValueEqual for Collection values is a thin wrapper
		// around Attributes.Equal. So the serious testing will be
		// performed there. Here we only test a couple of simple
		// cases.
		//
		// The same is true for ValueSimilar.
		{Collection{}, Collection{}, true, true},

		{
			v1: Collection{
				MakeAttr("attr1", TagInteger, Integer(1)),
				MakeAttr("attr2", TagString, String("hello")),
			},

			v2: Collection{
				MakeAttr("attr2", TagString, String("hello")),
				MakeAttr("attr1", TagInteger, Integer(1)),
			},

			equal:   false,
			similar: true,
		},
	}

	for _, test := range tests {
		equal := ValueEqual(test.v1, test.v2)
		similar := ValueSimilar(test.v1, test.v2)

		if equal != test.equal {
			t.Errorf("testing ValueEqual:\n"+
				"value 1:  %s\n"+
				"value 2:  %s\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.v1, test.v2,
				test.equal, equal,
			)
		}

		if similar != test.similar {
			t.Errorf("testing ValueSimilar:\n"+
				"value 1:  %s\n"+
				"value 2:  %s\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.v1, test.v2,
				test.similar, similar,
			)
		}
	}
}

// TestValuesString tests Values.String function
func TestValuesString(t *testing.T) {
	type testData struct {
		v Values // Input Values
		s string // Expected output
	}

	tests := []testData{
		{
			v: nil,
			s: "[]",
		},

		{
			v: Values{},
			s: "[]",
		},

		{
			v: Values{{TagInteger, Integer(5)}},
			s: "5",
		},

		{
			v: Values{{TagInteger, Integer(5)}, {TagEnum, Integer(6)}},
			s: "[5,6]",
		},
	}

	for _, test := range tests {
		s := test.v.String()
		if s != test.s {
			t.Errorf("testing Values.String:\n"+
				"value:    %#v\n"+
				"expected: %q\n"+
				"present:  %q\n",
				test.v, test.s, s,
			)
		}
	}
}

// TestValuesEqualSimilar tests Values.Equal and Values.Similar
func TestValuesEqualSimilar(t *testing.T) {
	type testData struct {
		v1, v2  Values // A pair of values
		equal   bool   // Expected v1.Equal(v2) output
		similar bool   // Expected v2.Similar(v2) output
	}

	tests := []testData{
		{
			v1:      nil,
			v2:      nil,
			equal:   true,
			similar: true,
		},

		{
			v1:      Values{},
			v2:      Values{},
			equal:   true,
			similar: true,
		},

		{
			v1:      Values{},
			v2:      nil,
			equal:   false,
			similar: true,
		},

		{
			v1:      Values{},
			v2:      Values{{TagInteger, Integer(5)}},
			equal:   false,
			similar: false,
		},

		{
			v1: Values{
				{TagInteger, Integer(5)},
				{TagEnum, Integer(6)},
			},
			v2: Values{
				{TagInteger, Integer(5)},
				{TagEnum, Integer(6)},
			},
			equal:   true,
			similar: true,
		},

		{
			v1: Values{
				{TagInteger, Integer(5)},
				{TagEnum, Integer(6)}},
			v2: Values{
				{TagInteger, Integer(5)},
				{TagInteger, Integer(6)}},
			equal:   false,
			similar: false,
		},

		{
			v1:      Values{{TagInteger, Integer(6)}, {TagEnum, Integer(5)}},
			v2:      Values{{TagInteger, Integer(5)}, {TagEnum, Integer(6)}},
			equal:   false,
			similar: false,
		},

		{
			v1: Values{
				{TagString, String("hello")},
				{TagString, Binary("world")},
			},
			v2: Values{
				{TagString, String("hello")},
				{TagString, Binary("world")},
			},
			equal:   true,
			similar: true,
		},

		{
			v1: Values{
				{TagString, Binary("hello")},
				{TagString, String("world")},
			},
			v2: Values{
				{TagString, String("hello")},
				{TagString, Binary("world")},
			},
			equal:   false,
			similar: true,
		},
	}

	for _, test := range tests {
		equal := test.v1.Equal(test.v2)
		similar := test.v1.Similar(test.v2)

		if equal != test.equal {
			t.Errorf("testing Values.Equal:\n"+
				"values 1:  %s\n"+
				"values 2:  %s\n"+
				"expected:  %v\n"+
				"present:   %v\n",
				test.v1, test.v2,
				test.equal, equal,
			)
		}

		if similar != test.similar {
			t.Errorf("testing Values.Similar:\n"+
				"values 1: %s\n"+
				"values 2: %s\n"+
				"expected: %v\n"+
				"present:  %v\n",
				test.v1, test.v2,
				test.similar, similar,
			)
		}
	}
}

// TestValuesCopy tests Values.Clone and Values.DeepCopy methods
func TestValuesCopy(t *testing.T) {
	values := Values{}
	values.Add(TagBoolean, Boolean(true))
	values.Add(TagExtension, Binary{})
	values.Add(TagString, Binary{1, 2, 3})
	values.Add(TagInteger, Integer(123))
	values.Add(TagEnum, Integer(-321))
	values.Add(TagRange, Range{-100, 200})
	values.Add(TagRange, Range{-100, -50})
	values.Add(TagResolution, Resolution{150, 300, UnitsDpi})
	values.Add(TagResolution, Resolution{100, 200, UnitsDpcm})
	values.Add(TagResolution, Resolution{75, 150, 10})
	values.Add(TagString, String("hello"))
	values.Add(TagTextLang, TextWithLang{"en-US", "hello"})
	values.Add(TagDateTime, Time{time.Now()})
	values.Add(TagNoValue, Void{})
	values.Add(TagBeginCollection,
		Collection{MakeAttribute("test", TagString, String(""))})

	type testData struct {
		values Values
	}

	tests := []testData{
		{nil},
		{Values{}},
		{values},
	}

	for _, test := range tests {
		clone := test.values.Clone()
		if !test.values.Equal(clone) {
			t.Errorf("testing Attributes.Clone\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.values,
				clone,
			)
		}

		copy := test.values.DeepCopy()
		if !test.values.Equal(copy) {
			t.Errorf("testing Attributes.DeepCopy\n"+
				"expected: %#v\n"+
				"present:  %#v\n",
				test.values,
				copy,
			)
		}
	}
}

// TestIntegerOrRange tests IntegerOrRange interface implementation
// by Integer and Range types
func TestIntegerOrRange(t *testing.T) {
	type testData struct {
		v      IntegerOrRange // Value being tested
		x      int            // IntegerOrRange.Within input
		within bool           // IntegerOrRange.Within expected output
	}

	tests := []testData{
		{
			v:      Integer(5),
			x:      5,
			within: true,
		},

		{
			v:      Integer(5),
			x:      6,
			within: false,
		},

		{
			v:      Range{-5, 5},
			x:      0,
			within: true,
		},

		{
			v:      Range{-5, 5},
			x:      -5,
			within: true,
		},

		{
			v:      Range{-5, 5},
			x:      5,
			within: true,
		},

		{
			v:      Range{-5, 5},
			x:      -6,
			within: false,
		},

		{
			v:      Range{-5, 5},
			x:      6,
			within: false,
		},
	}

	for _, test := range tests {
		within := test.v.Within(test.x)
		if within != test.within {
			t.Errorf("testing %s.Within:\n"+
				"value:    %#v\n"+
				"param:    %v\n"+
				"expected: %v\n"+
				"present:  %v\n",
				reflect.TypeOf(test.v).String(),
				test.v, test.x,
				test.within, within,
			)
		}
	}
}

// TestCollection tests Collection.Add method
func TestCollectionAdd(t *testing.T) {
	col1 := Collection{
		MakeAttr("attr1", TagInteger, Integer(1)),
		MakeAttr("attr2", TagString, String("hello")),
	}

	col2 := Collection{}
	col2.Add(MakeAttr("attr1", TagInteger, Integer(1)))
	col2.Add(MakeAttr("attr2", TagString, String("hello")))

	if !ValueEqual(col1, col2) {
		t.Errorf("Collection.Add test failed")
	}
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!588 blocks
openSUSE Build Service is sponsored by