1// Copyright 2015 The Go Authors.  All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:generate go run genarsc.go
6//go:generate stringer -output binres_string.go -type ResType,DataType
7
8// Package binres implements encoding and decoding of android binary resources.
9//
10// Binary resource structs support unmarshalling the binary output of aapt.
11// Implementations of marshalling for each struct must produce the exact input
12// sent to unmarshalling. This allows tests to validate each struct representation
13// of the binary format as follows:
14//
15//  * unmarshal the output of aapt
16//  * marshal the struct representation
17//  * perform byte-to-byte comparison with aapt output per chunk header and body
18//
19// This process should strive to make structs idiomatic to make parsing xml text
20// into structs trivial.
21//
22// Once the struct representation is validated, tests for parsing xml text
23// into structs can become self-referential as the following holds true:
24//
25//  * the unmarshalled input of aapt output is the only valid target
26//  * the unmarshalled input of xml text may be compared to the unmarshalled
27//    input of aapt output to identify errors, e.g. text-trims, wrong flags, etc
28//
29// This provides validation, byte-for-byte, for producing binary xml resources.
30//
31// It should be made clear that unmarshalling binary resources is currently only
32// in scope for proving that the BinaryMarshaler works correctly. Any other use
33// is currently out of scope.
34//
35// A simple view of binary xml document structure:
36//
37//  XML
38//    Pool
39//    Map
40//    Namespace
41//    [...node]
42//
43// Additional resources:
44// https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h
45// https://justanapplication.wordpress.com/2011/09/13/ (a series of articles, increment date)
46package binres
47
48import (
49	"encoding"
50	"encoding/binary"
51	"encoding/xml"
52	"fmt"
53	"io"
54	"sort"
55	"strconv"
56	"strings"
57	"unicode"
58)
59
60func errWrongType(have ResType, want ...ResType) error {
61	return fmt.Errorf("wrong resource type %s, want one of %v", have, want)
62}
63
64type ResType uint16
65
66func (t ResType) IsSupported() bool {
67	return t != ResNull
68}
69
70// explicitly defined for clarity and resolvability with apt source
71const (
72	ResNull       ResType = 0x0000
73	ResStringPool ResType = 0x0001
74	ResTable      ResType = 0x0002
75	ResXML        ResType = 0x0003
76
77	ResXMLStartNamespace ResType = 0x0100
78	ResXMLEndNamespace   ResType = 0x0101
79	ResXMLStartElement   ResType = 0x0102
80	ResXMLEndElement     ResType = 0x0103
81	ResXMLCharData       ResType = 0x0104
82
83	ResXMLResourceMap ResType = 0x0180
84
85	ResTablePackage  ResType = 0x0200
86	ResTableType     ResType = 0x0201
87	ResTableTypeSpec ResType = 0x0202
88	ResTableLibrary  ResType = 0x0203
89)
90
91var (
92	btou16 = binary.LittleEndian.Uint16
93	btou32 = binary.LittleEndian.Uint32
94	putu16 = binary.LittleEndian.PutUint16
95	putu32 = binary.LittleEndian.PutUint32
96)
97
98// unmarshaler wraps BinaryUnmarshaler to provide byte size of decoded chunks.
99type unmarshaler interface {
100	encoding.BinaryUnmarshaler
101
102	// size returns the byte size unmarshalled after a call to
103	// UnmarshalBinary, or otherwise zero.
104	size() int
105}
106
107// chunkHeader appears at the front of every data chunk in a resource.
108type chunkHeader struct {
109	// Type of data that follows this header.
110	typ ResType
111
112	// Advance slice index by this value to find its associated data, if any.
113	headerByteSize uint16
114
115	// This is the header size plus the size of any data associated with the chunk.
116	// Advance slice index by this value to completely skip its contents, including
117	// any child chunks. If this value is the same as headerByteSize, there is
118	// no data associated with the chunk.
119	byteSize uint32
120}
121
122// size implements unmarshaler.
123func (hdr chunkHeader) size() int { return int(hdr.byteSize) }
124
125func (hdr *chunkHeader) UnmarshalBinary(bin []byte) error {
126	hdr.typ = ResType(btou16(bin))
127	if !hdr.typ.IsSupported() {
128		return fmt.Errorf("%s not supported", hdr.typ)
129	}
130	hdr.headerByteSize = btou16(bin[2:])
131	hdr.byteSize = btou32(bin[4:])
132	if len(bin) < int(hdr.byteSize) {
133		return fmt.Errorf("too few bytes to unmarshal chunk body, have %v, need at-least %v", len(bin), hdr.byteSize)
134	}
135	return nil
136}
137
138func (hdr chunkHeader) MarshalBinary() ([]byte, error) {
139	if !hdr.typ.IsSupported() {
140		return nil, fmt.Errorf("%s not supported", hdr.typ)
141	}
142
143	bin := make([]byte, 8)
144	putu16(bin, uint16(hdr.typ))
145	putu16(bin[2:], hdr.headerByteSize)
146	putu32(bin[4:], hdr.byteSize)
147	return bin, nil
148}
149
150type XML struct {
151	chunkHeader
152
153	Pool *Pool
154	Map  *Map
155
156	Namespace *Namespace
157	Children  []*Element
158
159	// tmp field used when unmarshalling binary
160	stack []*Element
161}
162
163// RawValueByName returns the original raw string value of first matching element attribute, or error if not exists.
164// Given <manifest package="VAL" ...> then RawValueByName("manifest", xml.Name{Local: "package"}) returns "VAL".
165func (bx *XML) RawValueByName(elname string, attrname xml.Name) (string, error) {
166	elref, err := bx.Pool.RefByName(elname)
167	if err != nil {
168		return "", err
169	}
170	nref, err := bx.Pool.RefByName(attrname.Local)
171	if err != nil {
172		return "", err
173	}
174	nsref := PoolRef(NoEntry)
175	if attrname.Space != "" {
176		nsref, err = bx.Pool.RefByName(attrname.Space)
177		if err != nil {
178			return "", err
179		}
180	}
181
182	for el := range bx.iterElements() {
183		if el.Name == elref {
184			for _, attr := range el.attrs {
185				// TODO enforce TypedValue DataString constraint?
186				if nsref == attr.NS && nref == attr.Name {
187					return bx.Pool.strings[int(attr.RawValue)], nil
188				}
189			}
190		}
191	}
192	return "", fmt.Errorf("no matching element %q for attribute %+v found", elname, attrname)
193}
194
195const (
196	androidSchema = "http://schemas.android.com/apk/res/android"
197	toolsSchema   = "http://schemas.android.com/tools"
198)
199
200// skipSynthesize is set true for tests to avoid synthesis of additional nodes and attributes.
201var skipSynthesize bool
202
203// UnmarshalXML decodes an AndroidManifest.xml document returning type XML
204// containing decoded resources.
205func UnmarshalXML(r io.Reader, withIcon bool) (*XML, error) {
206	tbl, err := OpenTable()
207	if err != nil {
208		return nil, err
209	}
210
211	lr := &lineReader{r: r}
212	dec := xml.NewDecoder(lr)
213	bx := new(XML)
214
215	// temporary pool to resolve real poolref later
216	pool := new(Pool)
217
218	type ltoken struct {
219		xml.Token
220		line int
221	}
222	var q []ltoken
223
224	for {
225		line := lr.line(dec.InputOffset())
226		tkn, err := dec.Token()
227		if err != nil {
228			if err == io.EOF {
229				break
230			}
231			return nil, err
232		}
233		tkn = xml.CopyToken(tkn)
234
235		switch tkn := tkn.(type) {
236		case xml.StartElement:
237			switch tkn.Name.Local {
238			default:
239				q = append(q, ltoken{tkn, line})
240			case "uses-sdk":
241				return nil, fmt.Errorf("manual declaration of uses-sdk in AndroidManifest.xml not supported")
242			case "manifest":
243				// synthesize additional attributes and nodes for use during encode.
244				tkn.Attr = append(tkn.Attr,
245					xml.Attr{
246						Name: xml.Name{
247							Space: "",
248							Local: "platformBuildVersionCode",
249						},
250						Value: "15",
251					},
252					xml.Attr{
253						Name: xml.Name{
254							Space: "",
255							Local: "platformBuildVersionName",
256						},
257						Value: "4.0.4-1406430",
258					})
259
260				q = append(q, ltoken{tkn, line})
261
262				if !skipSynthesize {
263					s := xml.StartElement{
264						Name: xml.Name{
265							Space: "",
266							Local: "uses-sdk",
267						},
268						Attr: []xml.Attr{
269							xml.Attr{
270								Name: xml.Name{
271									Space: androidSchema,
272									Local: "minSdkVersion",
273								},
274								Value: fmt.Sprintf("%v", MinSDK),
275							},
276						},
277					}
278					e := xml.EndElement{Name: xml.Name{Local: "uses-sdk"}}
279
280					q = append(q, ltoken{s, line}, ltoken{e, line})
281				}
282			case "application":
283				if !skipSynthesize {
284					for _, attr := range tkn.Attr {
285						if attr.Name.Space == androidSchema && attr.Name.Local == "icon" {
286							return nil, fmt.Errorf("manual declaration of android:icon in AndroidManifest.xml not supported")
287						}
288					}
289					if withIcon {
290						tkn.Attr = append(tkn.Attr,
291							xml.Attr{
292								Name: xml.Name{
293									Space: androidSchema,
294									Local: "icon",
295								},
296								Value: "@mipmap/icon",
297							})
298					}
299				}
300				q = append(q, ltoken{tkn, line})
301			}
302		default:
303			q = append(q, ltoken{tkn, line})
304		}
305	}
306
307	for _, ltkn := range q {
308		tkn, line := ltkn.Token, ltkn.line
309
310		switch tkn := tkn.(type) {
311		case xml.StartElement:
312			el := &Element{
313				NodeHeader: NodeHeader{
314					LineNumber: uint32(line),
315					Comment:    0xFFFFFFFF,
316				},
317				NS:   NoEntry,
318				Name: pool.ref(tkn.Name.Local),
319			}
320			if len(bx.stack) == 0 {
321				bx.Children = append(bx.Children, el)
322			} else {
323				n := len(bx.stack)
324				var p *Element
325				p, bx.stack = bx.stack[n-1], bx.stack[:n-1]
326				p.Children = append(p.Children, el)
327				bx.stack = append(bx.stack, p)
328			}
329			bx.stack = append(bx.stack, el)
330
331			for _, attr := range tkn.Attr {
332				if (attr.Name.Space == "xmlns" && attr.Name.Local == "tools") || attr.Name.Space == toolsSchema {
333					continue // TODO can tbl be queried for schemas to determine validity instead?
334				}
335
336				if attr.Name.Space == "xmlns" && attr.Name.Local == "android" {
337					if bx.Namespace != nil {
338						return nil, fmt.Errorf("multiple declarations of xmlns:android encountered")
339					}
340					bx.Namespace = &Namespace{
341						NodeHeader: NodeHeader{
342							LineNumber: uint32(line),
343							Comment:    NoEntry,
344						},
345						prefix: 0,
346						uri:    0,
347					}
348					continue
349				}
350
351				nattr := &Attribute{
352					NS:       pool.ref(attr.Name.Space),
353					Name:     pool.ref(attr.Name.Local),
354					RawValue: NoEntry,
355				}
356				el.attrs = append(el.attrs, nattr)
357
358				if attr.Name.Space == "" {
359					nattr.NS = NoEntry
360					// TODO it's unclear how to query these
361					switch attr.Name.Local {
362					case "platformBuildVersionCode":
363						nattr.TypedValue.Type = DataIntDec
364						i, err := strconv.Atoi(attr.Value)
365						if err != nil {
366							return nil, err
367						}
368						nattr.TypedValue.Value = uint32(i)
369					default: // "package", "platformBuildVersionName", and any invalid
370						nattr.RawValue = pool.ref(attr.Value)
371						nattr.TypedValue.Type = DataString
372					}
373				} else {
374					// get type spec and value data type
375					ref, err := tbl.RefByName("attr/" + attr.Name.Local)
376					if err != nil {
377						return nil, err
378					}
379					nt, err := ref.Resolve(tbl)
380					if err != nil {
381						return nil, err
382					}
383					if len(nt.values) == 0 {
384						panic("encountered empty values slice")
385					}
386
387					if len(nt.values) == 1 {
388						val := nt.values[0]
389						if val.data.Type != DataIntDec {
390							panic("TODO only know how to handle DataIntDec type here")
391						}
392
393						t := DataType(val.data.Value)
394						switch t {
395						case DataString, DataAttribute, DataType(0x3e):
396							// TODO identify 0x3e, in bootstrap.xml this is the native lib name
397							nattr.RawValue = pool.ref(attr.Value)
398							nattr.TypedValue.Type = DataString
399							nattr.TypedValue.Value = uint32(nattr.RawValue)
400						case DataIntBool, DataType(0x08):
401							nattr.TypedValue.Type = DataIntBool
402							switch attr.Value {
403							case "true":
404								nattr.TypedValue.Value = 0xFFFFFFFF
405							case "false":
406								nattr.TypedValue.Value = 0
407							default:
408								return nil, fmt.Errorf("invalid bool value %q", attr.Value)
409							}
410						case DataIntDec, DataFloat, DataFraction:
411							// TODO DataFraction needs it's own case statement. minSdkVersion identifies as DataFraction
412							// but has accepted input in the past such as android:minSdkVersion="L"
413							// Other use-cases for DataFraction are currently unknown as applicable to manifest generation
414							// but this provides minimum support for writing out minSdkVersion="15" correctly.
415							nattr.TypedValue.Type = DataIntDec
416							i, err := strconv.Atoi(attr.Value)
417							if err != nil {
418								return nil, err
419							}
420							nattr.TypedValue.Value = uint32(i)
421						case DataReference:
422							nattr.TypedValue.Type = DataReference
423							dref, err := tbl.RefByName(attr.Value)
424							if err != nil {
425								if strings.HasPrefix(attr.Value, "@mipmap") {
426									// firstDrawableId is a TableRef matching first entry of mipmap spec initialized by NewMipmapTable.
427									// 7f is default package, 02 is mipmap spec, 0000 is first entry; e.g. R.drawable.icon
428									// TODO resource table should generate ids as required.
429									const firstDrawableId = 0x7f020000
430									nattr.TypedValue.Value = firstDrawableId
431									continue
432								}
433								return nil, err
434							}
435							nattr.TypedValue.Value = uint32(dref)
436						default:
437							return nil, fmt.Errorf("unhandled data type %0#2x: %s", uint8(t), t)
438						}
439					} else {
440						// 0x01000000 is an unknown ref that doesn't point to anything, typically
441						// located at the start of entry value lists, peek at last value to determine type.
442						t := nt.values[len(nt.values)-1].data.Type
443						switch t {
444						case DataIntDec:
445							for _, val := range nt.values {
446								if val.name == 0x01000000 {
447									continue
448								}
449								nr, err := val.name.Resolve(tbl)
450								if err != nil {
451									return nil, err
452								}
453								if attr.Value == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref
454									nattr.TypedValue = *val.data
455									break
456								}
457							}
458						case DataIntHex:
459							nattr.TypedValue.Type = t
460							for _, x := range strings.Split(attr.Value, "|") {
461								for _, val := range nt.values {
462									if val.name == 0x01000000 {
463										continue
464									}
465									nr, err := val.name.Resolve(tbl)
466									if err != nil {
467										return nil, err
468									}
469									if x == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref
470										nattr.TypedValue.Value |= val.data.Value
471										break
472									}
473								}
474							}
475						default:
476							return nil, fmt.Errorf("unhandled data type for configuration %0#2x: %s", uint8(t), t)
477						}
478					}
479				}
480			}
481		case xml.CharData:
482			if s := poolTrim(string(tkn)); s != "" {
483				cdt := &CharData{
484					NodeHeader: NodeHeader{
485						LineNumber: uint32(line),
486						Comment:    NoEntry,
487					},
488					RawData: pool.ref(s),
489				}
490				el := bx.stack[len(bx.stack)-1]
491				if el.head == nil {
492					el.head = cdt
493				} else if el.tail == nil {
494					el.tail = cdt
495				} else {
496					return nil, fmt.Errorf("element head and tail already contain chardata")
497				}
498			}
499		case xml.EndElement:
500			if tkn.Name.Local == "manifest" {
501				bx.Namespace.end = &Namespace{
502					NodeHeader: NodeHeader{
503						LineNumber: uint32(line),
504						Comment:    NoEntry,
505					},
506					prefix: 0,
507					uri:    0,
508				}
509			}
510			n := len(bx.stack)
511			var el *Element
512			el, bx.stack = bx.stack[n-1], bx.stack[:n-1]
513			if el.end != nil {
514				return nil, fmt.Errorf("element end already exists")
515			}
516			el.end = &ElementEnd{
517				NodeHeader: NodeHeader{
518					LineNumber: uint32(line),
519					Comment:    NoEntry,
520				},
521				NS:   el.NS,
522				Name: el.Name,
523			}
524		case xml.Comment, xml.ProcInst:
525			// discard
526		default:
527			panic(fmt.Errorf("unhandled token type: %T %+v", tkn, tkn))
528		}
529	}
530
531	// pools appear to be sorted as follows:
532	// * attribute names prefixed with android:
533	// * "android", [schema-url], [empty-string]
534	// * for each node:
535	//   * attribute names with no prefix
536	//   * node name
537	//   * attribute value if data type of name is DataString, DataAttribute, or 0x3e (an unknown)
538	bx.Pool = new(Pool)
539
540	var arecurse func(*Element)
541	arecurse = func(el *Element) {
542		for _, attr := range el.attrs {
543			if attr.NS == NoEntry {
544				continue
545			}
546			if attr.NS.Resolve(pool) == androidSchema {
547				bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
548			}
549		}
550		for _, child := range el.Children {
551			arecurse(child)
552		}
553	}
554	for _, el := range bx.Children {
555		arecurse(el)
556	}
557
558	// TODO encoding/xml does not enforce namespace prefix and manifest encoding in aapt
559	// appears to ignore all other prefixes. Inserting this manually is not strictly correct
560	// for the general case, but the effort to do otherwise currently offers nothing.
561	bx.Pool.strings = append(bx.Pool.strings, "android", androidSchema)
562
563	// there always appears to be an empty string located after schema, even if one is
564	// not present in manifest.
565	bx.Pool.strings = append(bx.Pool.strings, "")
566
567	var brecurse func(*Element)
568	brecurse = func(el *Element) {
569		for _, attr := range el.attrs {
570			if attr.NS == NoEntry {
571				bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
572			}
573		}
574
575		bx.Pool.strings = append(bx.Pool.strings, el.Name.Resolve(pool))
576
577		for _, attr := range el.attrs {
578			if attr.RawValue != NoEntry {
579				bx.Pool.strings = append(bx.Pool.strings, attr.RawValue.Resolve(pool))
580			} else if attr.NS == NoEntry {
581				bx.Pool.strings = append(bx.Pool.strings, fmt.Sprintf("%+v", attr.TypedValue.Value))
582			}
583		}
584
585		if el.head != nil {
586			bx.Pool.strings = append(bx.Pool.strings, el.head.RawData.Resolve(pool))
587		}
588		if el.tail != nil {
589			bx.Pool.strings = append(bx.Pool.strings, el.tail.RawData.Resolve(pool))
590		}
591
592		for _, child := range el.Children {
593			brecurse(child)
594		}
595	}
596	for _, el := range bx.Children {
597		brecurse(el)
598	}
599
600	// do not eliminate duplicates until the entire slice has been composed.
601	// consider <activity android:label="label" .../>
602	// all attribute names come first followed by values; in such a case, the value "label"
603	// would be a reference to the same "android:label" in the string pool which will occur
604	// within the beginning of the pool where other attr names are located.
605	bx.Pool.strings = asSet(bx.Pool.strings)
606
607	// TODO consider cases of multiple declarations of the same attr name that should return error
608	// before ever reaching this point.
609	bx.Map = new(Map)
610	for _, s := range bx.Pool.strings {
611		ref, err := tbl.RefByName("attr/" + s)
612		if err != nil {
613			break // break after first non-ref as all strings after are also non-refs.
614		}
615		bx.Map.rs = append(bx.Map.rs, ref)
616	}
617
618	// resolve tmp pool refs to final pool refs
619	// TODO drop this in favor of sort directly on Table
620	var resolve func(el *Element)
621	resolve = func(el *Element) {
622		if el.NS != NoEntry {
623			el.NS = bx.Pool.ref(el.NS.Resolve(pool))
624			el.end.NS = el.NS
625		}
626		el.Name = bx.Pool.ref(el.Name.Resolve(pool))
627		el.end.Name = el.Name
628		for _, attr := range el.attrs {
629			if attr.NS != NoEntry {
630				attr.NS = bx.Pool.ref(attr.NS.Resolve(pool))
631			}
632			attr.Name = bx.Pool.ref(attr.Name.Resolve(pool))
633			if attr.RawValue != NoEntry {
634				attr.RawValue = bx.Pool.ref(attr.RawValue.Resolve(pool))
635				if attr.TypedValue.Type == DataString {
636					attr.TypedValue.Value = uint32(attr.RawValue)
637				}
638			}
639		}
640		for _, child := range el.Children {
641			resolve(child)
642		}
643	}
644	for _, el := range bx.Children {
645		resolve(el)
646	}
647
648	var asort func(*Element)
649	asort = func(el *Element) {
650		sort.Sort(byType(el.attrs))
651		sort.Sort(byNamespace(el.attrs))
652		sort.Sort(byName(el.attrs))
653		for _, child := range el.Children {
654			asort(child)
655		}
656	}
657	for _, el := range bx.Children {
658		asort(el)
659	}
660
661	for i, s := range bx.Pool.strings {
662		switch s {
663		case androidSchema:
664			bx.Namespace.uri = PoolRef(i)
665			bx.Namespace.end.uri = PoolRef(i)
666		case "android":
667			bx.Namespace.prefix = PoolRef(i)
668			bx.Namespace.end.prefix = PoolRef(i)
669		}
670	}
671
672	return bx, nil
673}
674
675// UnmarshalBinary decodes all resource chunks in buf returning any error encountered.
676func (bx *XML) UnmarshalBinary(buf []byte) error {
677	if err := (&bx.chunkHeader).UnmarshalBinary(buf); err != nil {
678		return err
679	}
680	buf = buf[8:]
681	for len(buf) > 0 {
682		k, err := bx.unmarshalBinaryKind(buf)
683		if err != nil {
684			return err
685		}
686		buf = buf[k.size():]
687	}
688	return nil
689}
690
691// unmarshalBinaryKind decodes and stores the first resource chunk of bin.
692// It returns the unmarshaler interface and any error encountered.
693// If k.size() < len(bin), subsequent chunks can be decoded at bin[k.size():].
694func (bx *XML) unmarshalBinaryKind(bin []byte) (k unmarshaler, err error) {
695	k, err = bx.kind(ResType(btou16(bin)))
696	if err != nil {
697		return nil, err
698	}
699	if err = k.UnmarshalBinary(bin); err != nil {
700		return nil, err
701	}
702	return k, nil
703}
704
705func (bx *XML) kind(t ResType) (unmarshaler, error) {
706	switch t {
707	case ResStringPool:
708		if bx.Pool != nil {
709			return nil, fmt.Errorf("pool already exists")
710		}
711		bx.Pool = new(Pool)
712		return bx.Pool, nil
713	case ResXMLResourceMap:
714		if bx.Map != nil {
715			return nil, fmt.Errorf("resource map already exists")
716		}
717		bx.Map = new(Map)
718		return bx.Map, nil
719	case ResXMLStartNamespace:
720		if bx.Namespace != nil {
721			return nil, fmt.Errorf("namespace start already exists")
722		}
723		bx.Namespace = new(Namespace)
724		return bx.Namespace, nil
725	case ResXMLEndNamespace:
726		if bx.Namespace.end != nil {
727			return nil, fmt.Errorf("namespace end already exists")
728		}
729		bx.Namespace.end = new(Namespace)
730		return bx.Namespace.end, nil
731	case ResXMLStartElement:
732		el := new(Element)
733		if len(bx.stack) == 0 {
734			bx.Children = append(bx.Children, el)
735		} else {
736			n := len(bx.stack)
737			var p *Element
738			p, bx.stack = bx.stack[n-1], bx.stack[:n-1]
739			p.Children = append(p.Children, el)
740			bx.stack = append(bx.stack, p)
741		}
742		bx.stack = append(bx.stack, el)
743		return el, nil
744	case ResXMLEndElement:
745		n := len(bx.stack)
746		var el *Element
747		el, bx.stack = bx.stack[n-1], bx.stack[:n-1]
748		if el.end != nil {
749			return nil, fmt.Errorf("element end already exists")
750		}
751		el.end = new(ElementEnd)
752		return el.end, nil
753	case ResXMLCharData: // TODO assure correctness
754		cdt := new(CharData)
755		el := bx.stack[len(bx.stack)-1]
756		if el.head == nil {
757			el.head = cdt
758		} else if el.tail == nil {
759			el.tail = cdt
760		} else {
761			return nil, fmt.Errorf("element head and tail already contain chardata")
762		}
763		return cdt, nil
764	default:
765		return nil, fmt.Errorf("unexpected type %s", t)
766	}
767}
768
769func (bx *XML) MarshalBinary() ([]byte, error) {
770	bx.typ = ResXML
771	bx.headerByteSize = 8
772
773	var (
774		bin, b []byte
775		err    error
776	)
777	b, err = bx.chunkHeader.MarshalBinary()
778	if err != nil {
779		return nil, err
780	}
781	bin = append(bin, b...)
782
783	b, err = bx.Pool.MarshalBinary()
784	if err != nil {
785		return nil, err
786	}
787	bin = append(bin, b...)
788
789	b, err = bx.Map.MarshalBinary()
790	if err != nil {
791		return nil, err
792	}
793	bin = append(bin, b...)
794
795	b, err = bx.Namespace.MarshalBinary()
796	if err != nil {
797		return nil, err
798	}
799	bin = append(bin, b...)
800
801	for _, child := range bx.Children {
802		if err := marshalRecurse(child, &bin); err != nil {
803			return nil, err
804		}
805	}
806
807	b, err = bx.Namespace.end.MarshalBinary()
808	if err != nil {
809		return nil, err
810	}
811	bin = append(bin, b...)
812
813	putu32(bin[4:], uint32(len(bin)))
814	return bin, nil
815}
816
817func marshalRecurse(el *Element, bin *[]byte) error {
818	b, err := el.MarshalBinary()
819	if err != nil {
820		return err
821	}
822	*bin = append(*bin, b...)
823
824	if el.head != nil {
825		b, err := el.head.MarshalBinary()
826		if err != nil {
827			return err
828		}
829		*bin = append(*bin, b...)
830	}
831
832	for _, child := range el.Children {
833		if err := marshalRecurse(child, bin); err != nil {
834			return err
835		}
836	}
837
838	b, err = el.end.MarshalBinary()
839	if err != nil {
840		return err
841	}
842	*bin = append(*bin, b...)
843
844	return nil
845}
846
847func (bx *XML) iterElements() <-chan *Element {
848	ch := make(chan *Element, 1)
849	go func() {
850		for _, el := range bx.Children {
851			iterElementsRecurse(el, ch)
852		}
853		close(ch)
854	}()
855	return ch
856}
857
858func iterElementsRecurse(el *Element, ch chan *Element) {
859	ch <- el
860	for _, e := range el.Children {
861		iterElementsRecurse(e, ch)
862	}
863}
864
865// asSet returns a set from a slice of strings.
866func asSet(xs []string) []string {
867	m := make(map[string]bool)
868	fo := xs[:0]
869	for _, x := range xs {
870		if !m[x] {
871			m[x] = true
872			fo = append(fo, x)
873		}
874	}
875	return fo
876}
877
878// poolTrim trims all but immediately surrounding space.
879// \n\t\tfoobar\n\t\t becomes \tfoobar\n
880func poolTrim(s string) string {
881	var start, end int
882	for i, r := range s {
883		if !unicode.IsSpace(r) {
884			if i != 0 {
885				start = i - 1 // preserve preceding space
886			}
887			break
888		}
889	}
890
891	for i := len(s) - 1; i >= 0; i-- {
892		r := rune(s[i])
893		if !unicode.IsSpace(r) {
894			if i != len(s)-1 {
895				end = i + 2
896			}
897			break
898		}
899	}
900
901	if start == 0 && end == 0 {
902		return "" // every char was a space
903	}
904
905	return s[start:end]
906}
907
908// byNamespace sorts attributes based on string pool position of namespace.
909// Given that "android" always preceeds "" in the pool, this results in the
910// correct ordering of attributes.
911type byNamespace []*Attribute
912
913func (a byNamespace) Len() int { return len(a) }
914func (a byNamespace) Less(i, j int) bool {
915	return a[i].NS < a[j].NS
916}
917func (a byNamespace) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
918
919// byType sorts attributes by the uint8 value of the type.
920type byType []*Attribute
921
922func (a byType) Len() int { return len(a) }
923func (a byType) Less(i, j int) bool {
924	return a[i].TypedValue.Type < a[j].TypedValue.Type
925}
926func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
927
928// byName sorts attributes that have matching types based on string pool position of name.
929type byName []*Attribute
930
931func (a byName) Len() int { return len(a) }
932func (a byName) Less(i, j int) bool {
933	return (a[i].TypedValue.Type == DataString || a[i].TypedValue.Type == DataIntDec) &&
934		(a[j].TypedValue.Type == DataString || a[j].TypedValue.Type == DataIntDec) &&
935		a[i].Name < a[j].Name
936}
937func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
938
939type lineReader struct {
940	off   int64
941	lines []int64
942	r     io.Reader
943}
944
945func (r *lineReader) Read(p []byte) (n int, err error) {
946	n, err = r.r.Read(p)
947	for i := 0; i < n; i++ {
948		if p[i] == '\n' {
949			r.lines = append(r.lines, r.off+int64(i))
950		}
951	}
952	r.off += int64(n)
953	return n, err
954}
955
956func (r *lineReader) line(pos int64) int {
957	return sort.Search(len(r.lines), func(i int) bool {
958		return pos < r.lines[i]
959	}) + 1
960}
961