github.com/shranet/mobile@v0.0.0-20200814083559-5702cdcd481b/internal/binres/binres.go (about) 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) 46 package binres 47 48 import ( 49 "encoding" 50 "encoding/binary" 51 "encoding/xml" 52 "fmt" 53 "io" 54 "sort" 55 "strconv" 56 "strings" 57 "unicode" 58 ) 59 60 func errWrongType(have ResType, want ...ResType) error { 61 return fmt.Errorf("wrong resource type %s, want one of %v", have, want) 62 } 63 64 type ResType uint16 65 66 func (t ResType) IsSupported() bool { 67 return t != ResNull 68 } 69 70 // explicitly defined for clarity and resolvability with apt source 71 const ( 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 91 var ( 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. 99 type 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. 108 type 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. 123 func (hdr chunkHeader) size() int { return int(hdr.byteSize) } 124 125 func (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 138 func (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 150 type 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". 165 func (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 195 const ( 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. 201 var skipSynthesize bool 202 203 // UnmarshalXML decodes an AndroidManifest.xml document returning type XML 204 // containing decoded resources. 205 func 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. 676 func (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():]. 694 func (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 705 func (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 769 func (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 817 func 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 847 func (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 858 func 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. 866 func 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 880 func 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. 911 type byNamespace []*Attribute 912 913 func (a byNamespace) Len() int { return len(a) } 914 func (a byNamespace) Less(i, j int) bool { 915 return a[i].NS < a[j].NS 916 } 917 func (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. 920 type byType []*Attribute 921 922 func (a byType) Len() int { return len(a) } 923 func (a byType) Less(i, j int) bool { 924 return a[i].TypedValue.Type < a[j].TypedValue.Type 925 } 926 func (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. 929 type byName []*Attribute 930 931 func (a byName) Len() int { return len(a) } 932 func (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 } 937 func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 938 939 type lineReader struct { 940 off int64 941 lines []int64 942 r io.Reader 943 } 944 945 func (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 956 func (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 }