github.com/corfe83/mobile@v0.0.0-20220928034243-9edc37f43fac/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: "23", 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", 23), 275 }, 276 xml.Attr{ 277 Name: xml.Name{ 278 Space: androidSchema, 279 Local: "targetSdkVersion", 280 }, 281 Value: fmt.Sprintf("%v", 30), 282 }, 283 }, 284 } 285 e := xml.EndElement{Name: xml.Name{Local: "uses-sdk"}} 286 287 q = append(q, ltoken{s, line}, ltoken{e, line}) 288 } 289 case "application": 290 if !skipSynthesize { 291 for _, attr := range tkn.Attr { 292 if attr.Name.Space == androidSchema && attr.Name.Local == "icon" { 293 return nil, fmt.Errorf("manual declaration of android:icon in AndroidManifest.xml not supported") 294 } 295 } 296 if withIcon { 297 tkn.Attr = append(tkn.Attr, 298 xml.Attr{ 299 Name: xml.Name{ 300 Space: androidSchema, 301 Local: "icon", 302 }, 303 Value: "@mipmap/icon", 304 }) 305 } 306 } 307 q = append(q, ltoken{tkn, line}) 308 } 309 default: 310 q = append(q, ltoken{tkn, line}) 311 } 312 } 313 314 for _, ltkn := range q { 315 tkn, line := ltkn.Token, ltkn.line 316 317 switch tkn := tkn.(type) { 318 case xml.StartElement: 319 el := &Element{ 320 NodeHeader: NodeHeader{ 321 LineNumber: uint32(line), 322 Comment: 0xFFFFFFFF, 323 }, 324 NS: NoEntry, 325 Name: pool.ref(tkn.Name.Local), 326 } 327 if len(bx.stack) == 0 { 328 bx.Children = append(bx.Children, el) 329 } else { 330 n := len(bx.stack) 331 var p *Element 332 p, bx.stack = bx.stack[n-1], bx.stack[:n-1] 333 p.Children = append(p.Children, el) 334 bx.stack = append(bx.stack, p) 335 } 336 bx.stack = append(bx.stack, el) 337 338 for _, attr := range tkn.Attr { 339 if (attr.Name.Space == "xmlns" && attr.Name.Local == "tools") || attr.Name.Space == toolsSchema { 340 continue // TODO can tbl be queried for schemas to determine validity instead? 341 } 342 343 if attr.Name.Space == "xmlns" && attr.Name.Local == "android" { 344 if bx.Namespace != nil { 345 return nil, fmt.Errorf("multiple declarations of xmlns:android encountered") 346 } 347 bx.Namespace = &Namespace{ 348 NodeHeader: NodeHeader{ 349 LineNumber: uint32(line), 350 Comment: NoEntry, 351 }, 352 prefix: 0, 353 uri: 0, 354 } 355 continue 356 } 357 358 nattr := &Attribute{ 359 NS: pool.ref(attr.Name.Space), 360 Name: pool.ref(attr.Name.Local), 361 RawValue: NoEntry, 362 } 363 el.attrs = append(el.attrs, nattr) 364 365 if attr.Name.Space == "" { 366 nattr.NS = NoEntry 367 // TODO it's unclear how to query these 368 switch attr.Name.Local { 369 case "platformBuildVersionCode": 370 nattr.TypedValue.Type = DataIntDec 371 i, err := strconv.Atoi(attr.Value) 372 if err != nil { 373 return nil, err 374 } 375 nattr.TypedValue.Value = uint32(i) 376 default: // "package", "platformBuildVersionName", and any invalid 377 nattr.RawValue = pool.ref(attr.Value) 378 nattr.TypedValue.Type = DataString 379 } 380 } else { 381 // get type spec and value data type 382 ref, err := tbl.RefByName("attr/" + attr.Name.Local) 383 if err != nil { 384 return nil, err 385 } 386 nt, err := ref.Resolve(tbl) 387 if err != nil { 388 return nil, err 389 } 390 if len(nt.values) == 0 { 391 panic("encountered empty values slice") 392 } 393 394 if len(nt.values) == 1 { 395 val := nt.values[0] 396 if val.data.Type != DataIntDec { 397 panic("TODO only know how to handle DataIntDec type here") 398 } 399 400 t := DataType(val.data.Value) 401 switch t { 402 case DataString, DataAttribute, DataType(0x3e): 403 // TODO identify 0x3e, in bootstrap.xml this is the native lib name 404 nattr.RawValue = pool.ref(attr.Value) 405 nattr.TypedValue.Type = DataString 406 nattr.TypedValue.Value = uint32(nattr.RawValue) 407 case DataIntBool, DataType(0x08): 408 nattr.TypedValue.Type = DataIntBool 409 switch attr.Value { 410 case "true": 411 nattr.TypedValue.Value = 0xFFFFFFFF 412 case "false": 413 nattr.TypedValue.Value = 0 414 default: 415 return nil, fmt.Errorf("invalid bool value %q", attr.Value) 416 } 417 case DataIntDec, DataFloat, DataFraction: 418 // TODO DataFraction needs it's own case statement. minSdkVersion identifies as DataFraction 419 // but has accepted input in the past such as android:minSdkVersion="L" 420 // Other use-cases for DataFraction are currently unknown as applicable to manifest generation 421 // but this provides minimum support for writing out minSdkVersion="15" correctly. 422 nattr.TypedValue.Type = DataIntDec 423 i, err := strconv.Atoi(attr.Value) 424 if err != nil { 425 return nil, err 426 } 427 nattr.TypedValue.Value = uint32(i) 428 case DataReference: 429 nattr.TypedValue.Type = DataReference 430 dref, err := tbl.RefByName(attr.Value) 431 if err != nil { 432 if strings.HasPrefix(attr.Value, "@mipmap") { 433 // firstDrawableId is a TableRef matching first entry of mipmap spec initialized by NewMipmapTable. 434 // 7f is default package, 02 is mipmap spec, 0000 is first entry; e.g. R.drawable.icon 435 // TODO resource table should generate ids as required. 436 const firstDrawableId = 0x7f020000 437 nattr.TypedValue.Value = firstDrawableId 438 continue 439 } 440 return nil, err 441 } 442 nattr.TypedValue.Value = uint32(dref) 443 default: 444 return nil, fmt.Errorf("unhandled data type %0#2x: %s", uint8(t), t) 445 } 446 } else { 447 // 0x01000000 is an unknown ref that doesn't point to anything, typically 448 // located at the start of entry value lists, peek at last value to determine type. 449 t := nt.values[len(nt.values)-1].data.Type 450 switch t { 451 case DataIntDec: 452 for _, val := range nt.values { 453 if val.name == 0x01000000 { 454 continue 455 } 456 nr, err := val.name.Resolve(tbl) 457 if err != nil { 458 return nil, err 459 } 460 if attr.Value == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref 461 nattr.TypedValue = *val.data 462 break 463 } 464 } 465 case DataIntHex: 466 nattr.TypedValue.Type = t 467 for _, x := range strings.Split(attr.Value, "|") { 468 for _, val := range nt.values { 469 if val.name == 0x01000000 { 470 continue 471 } 472 nr, err := val.name.Resolve(tbl) 473 if err != nil { 474 return nil, err 475 } 476 if x == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref 477 nattr.TypedValue.Value |= val.data.Value 478 break 479 } 480 } 481 } 482 default: 483 return nil, fmt.Errorf("unhandled data type for configuration %0#2x: %s", uint8(t), t) 484 } 485 } 486 } 487 } 488 case xml.CharData: 489 if s := poolTrim(string(tkn)); s != "" { 490 cdt := &CharData{ 491 NodeHeader: NodeHeader{ 492 LineNumber: uint32(line), 493 Comment: NoEntry, 494 }, 495 RawData: pool.ref(s), 496 } 497 el := bx.stack[len(bx.stack)-1] 498 if el.head == nil { 499 el.head = cdt 500 } else if el.tail == nil { 501 el.tail = cdt 502 } else { 503 return nil, fmt.Errorf("element head and tail already contain chardata") 504 } 505 } 506 case xml.EndElement: 507 if tkn.Name.Local == "manifest" { 508 bx.Namespace.end = &Namespace{ 509 NodeHeader: NodeHeader{ 510 LineNumber: uint32(line), 511 Comment: NoEntry, 512 }, 513 prefix: 0, 514 uri: 0, 515 } 516 } 517 n := len(bx.stack) 518 var el *Element 519 el, bx.stack = bx.stack[n-1], bx.stack[:n-1] 520 if el.end != nil { 521 return nil, fmt.Errorf("element end already exists") 522 } 523 el.end = &ElementEnd{ 524 NodeHeader: NodeHeader{ 525 LineNumber: uint32(line), 526 Comment: NoEntry, 527 }, 528 NS: el.NS, 529 Name: el.Name, 530 } 531 case xml.Comment, xml.ProcInst: 532 // discard 533 default: 534 panic(fmt.Errorf("unhandled token type: %T %+v", tkn, tkn)) 535 } 536 } 537 538 // pools appear to be sorted as follows: 539 // * attribute names prefixed with android: 540 // * "android", [schema-url], [empty-string] 541 // * for each node: 542 // * attribute names with no prefix 543 // * node name 544 // * attribute value if data type of name is DataString, DataAttribute, or 0x3e (an unknown) 545 bx.Pool = new(Pool) 546 547 var arecurse func(*Element) 548 arecurse = func(el *Element) { 549 for _, attr := range el.attrs { 550 if attr.NS == NoEntry { 551 continue 552 } 553 if attr.NS.Resolve(pool) == androidSchema { 554 bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool)) 555 } 556 } 557 for _, child := range el.Children { 558 arecurse(child) 559 } 560 } 561 for _, el := range bx.Children { 562 arecurse(el) 563 } 564 565 // TODO encoding/xml does not enforce namespace prefix and manifest encoding in aapt 566 // appears to ignore all other prefixes. Inserting this manually is not strictly correct 567 // for the general case, but the effort to do otherwise currently offers nothing. 568 bx.Pool.strings = append(bx.Pool.strings, "android", androidSchema) 569 570 // there always appears to be an empty string located after schema, even if one is 571 // not present in manifest. 572 bx.Pool.strings = append(bx.Pool.strings, "") 573 574 var brecurse func(*Element) 575 brecurse = func(el *Element) { 576 for _, attr := range el.attrs { 577 if attr.NS == NoEntry { 578 bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool)) 579 } 580 } 581 582 bx.Pool.strings = append(bx.Pool.strings, el.Name.Resolve(pool)) 583 584 for _, attr := range el.attrs { 585 if attr.RawValue != NoEntry { 586 bx.Pool.strings = append(bx.Pool.strings, attr.RawValue.Resolve(pool)) 587 } else if attr.NS == NoEntry { 588 bx.Pool.strings = append(bx.Pool.strings, fmt.Sprintf("%+v", attr.TypedValue.Value)) 589 } 590 } 591 592 if el.head != nil { 593 bx.Pool.strings = append(bx.Pool.strings, el.head.RawData.Resolve(pool)) 594 } 595 if el.tail != nil { 596 bx.Pool.strings = append(bx.Pool.strings, el.tail.RawData.Resolve(pool)) 597 } 598 599 for _, child := range el.Children { 600 brecurse(child) 601 } 602 } 603 for _, el := range bx.Children { 604 brecurse(el) 605 } 606 607 // do not eliminate duplicates until the entire slice has been composed. 608 // consider <activity android:label="label" .../> 609 // all attribute names come first followed by values; in such a case, the value "label" 610 // would be a reference to the same "android:label" in the string pool which will occur 611 // within the beginning of the pool where other attr names are located. 612 bx.Pool.strings = asSet(bx.Pool.strings) 613 614 // TODO consider cases of multiple declarations of the same attr name that should return error 615 // before ever reaching this point. 616 bx.Map = new(Map) 617 for _, s := range bx.Pool.strings { 618 ref, err := tbl.RefByName("attr/" + s) 619 if err != nil { 620 break // break after first non-ref as all strings after are also non-refs. 621 } 622 bx.Map.rs = append(bx.Map.rs, ref) 623 } 624 625 // resolve tmp pool refs to final pool refs 626 // TODO drop this in favor of sort directly on Table 627 var resolve func(el *Element) 628 resolve = func(el *Element) { 629 if el.NS != NoEntry { 630 el.NS = bx.Pool.ref(el.NS.Resolve(pool)) 631 el.end.NS = el.NS 632 } 633 el.Name = bx.Pool.ref(el.Name.Resolve(pool)) 634 el.end.Name = el.Name 635 for _, attr := range el.attrs { 636 if attr.NS != NoEntry { 637 attr.NS = bx.Pool.ref(attr.NS.Resolve(pool)) 638 } 639 attr.Name = bx.Pool.ref(attr.Name.Resolve(pool)) 640 if attr.RawValue != NoEntry { 641 attr.RawValue = bx.Pool.ref(attr.RawValue.Resolve(pool)) 642 if attr.TypedValue.Type == DataString { 643 attr.TypedValue.Value = uint32(attr.RawValue) 644 } 645 } 646 } 647 for _, child := range el.Children { 648 resolve(child) 649 } 650 } 651 for _, el := range bx.Children { 652 resolve(el) 653 } 654 655 var asort func(*Element) 656 asort = func(el *Element) { 657 sort.Sort(byType(el.attrs)) 658 sort.Sort(byNamespace(el.attrs)) 659 sort.Sort(byName(el.attrs)) 660 for _, child := range el.Children { 661 asort(child) 662 } 663 } 664 for _, el := range bx.Children { 665 asort(el) 666 } 667 668 for i, s := range bx.Pool.strings { 669 switch s { 670 case androidSchema: 671 bx.Namespace.uri = PoolRef(i) 672 bx.Namespace.end.uri = PoolRef(i) 673 case "android": 674 bx.Namespace.prefix = PoolRef(i) 675 bx.Namespace.end.prefix = PoolRef(i) 676 } 677 } 678 679 return bx, nil 680 } 681 682 // UnmarshalBinary decodes all resource chunks in buf returning any error encountered. 683 func (bx *XML) UnmarshalBinary(buf []byte) error { 684 if err := (&bx.chunkHeader).UnmarshalBinary(buf); err != nil { 685 return err 686 } 687 buf = buf[8:] 688 for len(buf) > 0 { 689 k, err := bx.unmarshalBinaryKind(buf) 690 if err != nil { 691 return err 692 } 693 buf = buf[k.size():] 694 } 695 return nil 696 } 697 698 // unmarshalBinaryKind decodes and stores the first resource chunk of bin. 699 // It returns the unmarshaler interface and any error encountered. 700 // If k.size() < len(bin), subsequent chunks can be decoded at bin[k.size():]. 701 func (bx *XML) unmarshalBinaryKind(bin []byte) (k unmarshaler, err error) { 702 k, err = bx.kind(ResType(btou16(bin))) 703 if err != nil { 704 return nil, err 705 } 706 if err = k.UnmarshalBinary(bin); err != nil { 707 return nil, err 708 } 709 return k, nil 710 } 711 712 func (bx *XML) kind(t ResType) (unmarshaler, error) { 713 switch t { 714 case ResStringPool: 715 if bx.Pool != nil { 716 return nil, fmt.Errorf("pool already exists") 717 } 718 bx.Pool = new(Pool) 719 return bx.Pool, nil 720 case ResXMLResourceMap: 721 if bx.Map != nil { 722 return nil, fmt.Errorf("resource map already exists") 723 } 724 bx.Map = new(Map) 725 return bx.Map, nil 726 case ResXMLStartNamespace: 727 if bx.Namespace != nil { 728 return nil, fmt.Errorf("namespace start already exists") 729 } 730 bx.Namespace = new(Namespace) 731 return bx.Namespace, nil 732 case ResXMLEndNamespace: 733 if bx.Namespace.end != nil { 734 return nil, fmt.Errorf("namespace end already exists") 735 } 736 bx.Namespace.end = new(Namespace) 737 return bx.Namespace.end, nil 738 case ResXMLStartElement: 739 el := new(Element) 740 if len(bx.stack) == 0 { 741 bx.Children = append(bx.Children, el) 742 } else { 743 n := len(bx.stack) 744 var p *Element 745 p, bx.stack = bx.stack[n-1], bx.stack[:n-1] 746 p.Children = append(p.Children, el) 747 bx.stack = append(bx.stack, p) 748 } 749 bx.stack = append(bx.stack, el) 750 return el, nil 751 case ResXMLEndElement: 752 n := len(bx.stack) 753 var el *Element 754 el, bx.stack = bx.stack[n-1], bx.stack[:n-1] 755 if el.end != nil { 756 return nil, fmt.Errorf("element end already exists") 757 } 758 el.end = new(ElementEnd) 759 return el.end, nil 760 case ResXMLCharData: // TODO assure correctness 761 cdt := new(CharData) 762 el := bx.stack[len(bx.stack)-1] 763 if el.head == nil { 764 el.head = cdt 765 } else if el.tail == nil { 766 el.tail = cdt 767 } else { 768 return nil, fmt.Errorf("element head and tail already contain chardata") 769 } 770 return cdt, nil 771 default: 772 return nil, fmt.Errorf("unexpected type %s", t) 773 } 774 } 775 776 func (bx *XML) MarshalBinary() ([]byte, error) { 777 bx.typ = ResXML 778 bx.headerByteSize = 8 779 780 var ( 781 bin, b []byte 782 err error 783 ) 784 b, err = bx.chunkHeader.MarshalBinary() 785 if err != nil { 786 return nil, err 787 } 788 bin = append(bin, b...) 789 790 b, err = bx.Pool.MarshalBinary() 791 if err != nil { 792 return nil, err 793 } 794 bin = append(bin, b...) 795 796 b, err = bx.Map.MarshalBinary() 797 if err != nil { 798 return nil, err 799 } 800 bin = append(bin, b...) 801 802 b, err = bx.Namespace.MarshalBinary() 803 if err != nil { 804 return nil, err 805 } 806 bin = append(bin, b...) 807 808 for _, child := range bx.Children { 809 if err := marshalRecurse(child, &bin); err != nil { 810 return nil, err 811 } 812 } 813 814 b, err = bx.Namespace.end.MarshalBinary() 815 if err != nil { 816 return nil, err 817 } 818 bin = append(bin, b...) 819 820 putu32(bin[4:], uint32(len(bin))) 821 return bin, nil 822 } 823 824 func marshalRecurse(el *Element, bin *[]byte) error { 825 b, err := el.MarshalBinary() 826 if err != nil { 827 return err 828 } 829 *bin = append(*bin, b...) 830 831 if el.head != nil { 832 b, err := el.head.MarshalBinary() 833 if err != nil { 834 return err 835 } 836 *bin = append(*bin, b...) 837 } 838 839 for _, child := range el.Children { 840 if err := marshalRecurse(child, bin); err != nil { 841 return err 842 } 843 } 844 845 b, err = el.end.MarshalBinary() 846 if err != nil { 847 return err 848 } 849 *bin = append(*bin, b...) 850 851 return nil 852 } 853 854 func (bx *XML) iterElements() <-chan *Element { 855 ch := make(chan *Element, 1) 856 go func() { 857 for _, el := range bx.Children { 858 iterElementsRecurse(el, ch) 859 } 860 close(ch) 861 }() 862 return ch 863 } 864 865 func iterElementsRecurse(el *Element, ch chan *Element) { 866 ch <- el 867 for _, e := range el.Children { 868 iterElementsRecurse(e, ch) 869 } 870 } 871 872 // asSet returns a set from a slice of strings. 873 func asSet(xs []string) []string { 874 m := make(map[string]bool) 875 fo := xs[:0] 876 for _, x := range xs { 877 if !m[x] { 878 m[x] = true 879 fo = append(fo, x) 880 } 881 } 882 return fo 883 } 884 885 // poolTrim trims all but immediately surrounding space. 886 // \n\t\tfoobar\n\t\t becomes \tfoobar\n 887 func poolTrim(s string) string { 888 var start, end int 889 for i, r := range s { 890 if !unicode.IsSpace(r) { 891 if i != 0 { 892 start = i - 1 // preserve preceding space 893 } 894 break 895 } 896 } 897 898 for i := len(s) - 1; i >= 0; i-- { 899 r := rune(s[i]) 900 if !unicode.IsSpace(r) { 901 if i != len(s)-1 { 902 end = i + 2 903 } 904 break 905 } 906 } 907 908 if start == 0 && end == 0 { 909 return "" // every char was a space 910 } 911 912 return s[start:end] 913 } 914 915 // byNamespace sorts attributes based on string pool position of namespace. 916 // Given that "android" always preceeds "" in the pool, this results in the 917 // correct ordering of attributes. 918 type byNamespace []*Attribute 919 920 func (a byNamespace) Len() int { return len(a) } 921 func (a byNamespace) Less(i, j int) bool { 922 return a[i].NS < a[j].NS 923 } 924 func (a byNamespace) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 925 926 // byType sorts attributes by the uint8 value of the type. 927 type byType []*Attribute 928 929 func (a byType) Len() int { return len(a) } 930 func (a byType) Less(i, j int) bool { 931 return a[i].TypedValue.Type < a[j].TypedValue.Type 932 } 933 func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 934 935 // byName sorts attributes that have matching types based on string pool position of name. 936 type byName []*Attribute 937 938 func (a byName) Len() int { return len(a) } 939 func (a byName) Less(i, j int) bool { 940 return (a[i].TypedValue.Type == DataString || a[i].TypedValue.Type == DataIntDec) && 941 (a[j].TypedValue.Type == DataString || a[j].TypedValue.Type == DataIntDec) && 942 a[i].Name < a[j].Name 943 } 944 func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 945 946 type lineReader struct { 947 off int64 948 lines []int64 949 r io.Reader 950 } 951 952 func (r *lineReader) Read(p []byte) (n int, err error) { 953 n, err = r.r.Read(p) 954 for i := 0; i < n; i++ { 955 if p[i] == '\n' { 956 r.lines = append(r.lines, r.off+int64(i)) 957 } 958 } 959 r.off += int64(n) 960 return n, err 961 } 962 963 func (r *lineReader) line(pos int64) int { 964 return sort.Search(len(r.lines), func(i int) bool { 965 return pos < r.lines[i] 966 }) + 1 967 }