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