github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/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 stringer -output binres_string.go -type ResType,DataType
     6  
     7  // Package binres implements encoding and decoding of android binary resources.
     8  //
     9  // Binary resource structs support unmarshalling the binary output of aapt.
    10  // Implementations of marshalling for each struct must produce the exact input
    11  // sent to unmarshalling. This allows tests to validate each struct representation
    12  // of the binary format as follows:
    13  //
    14  //  * unmarshal the output of aapt
    15  //  * marshal the struct representation
    16  //  * perform byte-to-byte comparison with aapt output per chunk header and body
    17  //
    18  // This process should strive to make structs idiomatic to make parsing xml text
    19  // into structs trivial.
    20  //
    21  // Once the struct representation is validated, tests for parsing xml text
    22  // into structs can become self-referential as the following holds true:
    23  //
    24  //  * the unmarshalled input of aapt output is the only valid target
    25  //  * the unmarshalled input of xml text may be compared to the unmarshalled
    26  //    input of aapt output to identify errors, e.g. text-trims, wrong flags, etc
    27  //
    28  // This provides validation, byte-for-byte, for producing binary xml resources.
    29  //
    30  // It should be made clear that unmarshalling binary resources is currently only
    31  // in scope for proving that the BinaryMarshaler works correctly. Any other use
    32  // is currently out of scope.
    33  //
    34  // A simple view of binary xml document structure:
    35  //
    36  //  XML
    37  //    Pool
    38  //    Map
    39  //    Namespace
    40  //    [...node]
    41  //
    42  // Additional resources:
    43  // https://android.googlesource.com/platform/frameworks/base/+/master/include/androidfw/ResourceTypes.h
    44  // https://justanapplication.wordpress.com/2011/09/13/ (a series of articles, increment date)
    45  package binres
    46  
    47  import (
    48  	"encoding"
    49  	"encoding/binary"
    50  	"fmt"
    51  )
    52  
    53  type ResType uint16
    54  
    55  func (t ResType) IsSupported() bool {
    56  	// explicit for clarity
    57  	return t == ResStringPool || t == ResXML ||
    58  		t == ResXMLStartNamespace || t == ResXMLEndNamespace ||
    59  		t == ResXMLStartElement || t == ResXMLEndElement ||
    60  		t == ResXMLCharData ||
    61  		t == ResXMLResourceMap ||
    62  		t == ResTable || t == ResTablePackage
    63  }
    64  
    65  // explicitly defined for clarity and resolvability with apt source
    66  const (
    67  	ResNull       ResType = 0x0000
    68  	ResStringPool ResType = 0x0001
    69  	ResTable      ResType = 0x0002
    70  	ResXML        ResType = 0x0003
    71  
    72  	ResXMLStartNamespace ResType = 0x0100
    73  	ResXMLEndNamespace   ResType = 0x0101
    74  	ResXMLStartElement   ResType = 0x0102
    75  	ResXMLEndElement     ResType = 0x0103
    76  	ResXMLCharData       ResType = 0x0104
    77  
    78  	ResXMLResourceMap ResType = 0x0180
    79  
    80  	ResTablePackage  ResType = 0x0200
    81  	ResTableType     ResType = 0x0201
    82  	ResTableTypeSpec ResType = 0x0202
    83  )
    84  
    85  var (
    86  	btou16 = binary.LittleEndian.Uint16
    87  	btou32 = binary.LittleEndian.Uint32
    88  	putu16 = binary.LittleEndian.PutUint16
    89  	putu32 = binary.LittleEndian.PutUint32
    90  )
    91  
    92  // unmarshaler wraps BinaryUnmarshaler to provide byte size of decoded chunks.
    93  type unmarshaler interface {
    94  	encoding.BinaryUnmarshaler
    95  
    96  	// size returns the byte size unmarshalled after a call to
    97  	// UnmarshalBinary, or otherwise zero.
    98  	size() int
    99  }
   100  
   101  // chunkHeader appears at the front of every data chunk in a resource.
   102  // TODO look into removing this, it's not necessary for marshalling and
   103  // the information provided may possibly be given more simply. For unmarshal,
   104  // a simple function would do.
   105  type chunkHeader struct {
   106  	// Type of data that follows this header.
   107  	typ ResType
   108  
   109  	// Advance slice index by this value to find its associated data, if any.
   110  	headerByteSize uint16
   111  
   112  	// This is the header size plus the size of any data associated with the chunk.
   113  	// Advance slice index by this value to completely skip its contents, including
   114  	// any child chunks. If this value is the same as headerByteSize, there is
   115  	// no data associated with the chunk.
   116  	byteSize uint32
   117  }
   118  
   119  // size implements unmarshaler.
   120  func (hdr chunkHeader) size() int { return int(hdr.byteSize) }
   121  
   122  func (hdr *chunkHeader) UnmarshalBinary(bin []byte) error {
   123  	hdr.typ = ResType(btou16(bin))
   124  	if !hdr.typ.IsSupported() {
   125  		return fmt.Errorf("%s not supported", hdr.typ)
   126  	}
   127  	hdr.headerByteSize = btou16(bin[2:])
   128  	hdr.byteSize = btou32(bin[4:])
   129  	return nil
   130  }
   131  
   132  func (hdr chunkHeader) MarshalBinary() ([]byte, error) {
   133  	if !hdr.typ.IsSupported() {
   134  		return nil, fmt.Errorf("%s not supported", hdr.typ)
   135  	}
   136  	bin := make([]byte, 8)
   137  	putu16(bin, uint16(hdr.typ))
   138  	putu16(bin[2:], hdr.headerByteSize)
   139  	putu32(bin[4:], hdr.byteSize)
   140  	return bin, nil
   141  }
   142  
   143  type XML struct {
   144  	chunkHeader
   145  
   146  	Pool *Pool
   147  	Map  *Map
   148  
   149  	Namespace *Namespace
   150  	Children  []*Element
   151  
   152  	// tmp field used when unmarshalling
   153  	stack []*Element
   154  }
   155  
   156  // TODO this is used strictly for querying in tests and dependent on
   157  // current XML.UnmarshalBinary implementation. Look into moving directly
   158  // into tests.
   159  var debugIndices = make(map[encoding.BinaryMarshaler]int)
   160  
   161  func (bx *XML) UnmarshalBinary(bin []byte) error {
   162  	buf := bin
   163  	if err := (&bx.chunkHeader).UnmarshalBinary(bin); err != nil {
   164  		return err
   165  	}
   166  	buf = buf[8:]
   167  
   168  	// TODO this is tracked strictly for querying in tests; look into moving this
   169  	// functionality directly into tests if possible.
   170  	debugIndex := 8
   171  
   172  	for len(buf) > 0 {
   173  		t := ResType(btou16(buf))
   174  		k, err := bx.kind(t)
   175  		if err != nil {
   176  			return err
   177  		}
   178  		if err := k.UnmarshalBinary(buf); err != nil {
   179  			return err
   180  		}
   181  		debugIndices[k.(encoding.BinaryMarshaler)] = debugIndex
   182  		debugIndex += int(k.size())
   183  		buf = buf[k.size():]
   184  	}
   185  	return nil
   186  }
   187  
   188  func (bx *XML) kind(t ResType) (unmarshaler, error) {
   189  	switch t {
   190  	case ResStringPool:
   191  		if bx.Pool != nil {
   192  			return nil, fmt.Errorf("pool already exists")
   193  		}
   194  		bx.Pool = new(Pool)
   195  		return bx.Pool, nil
   196  	case ResXMLResourceMap:
   197  		if bx.Map != nil {
   198  			return nil, fmt.Errorf("resource map already exists")
   199  		}
   200  		bx.Map = new(Map)
   201  		return bx.Map, nil
   202  	case ResXMLStartNamespace:
   203  		if bx.Namespace != nil {
   204  			return nil, fmt.Errorf("namespace start already exists")
   205  		}
   206  		bx.Namespace = new(Namespace)
   207  		return bx.Namespace, nil
   208  	case ResXMLEndNamespace:
   209  		if bx.Namespace.end != nil {
   210  			return nil, fmt.Errorf("namespace end already exists")
   211  		}
   212  		bx.Namespace.end = new(Namespace)
   213  		return bx.Namespace.end, nil
   214  	case ResXMLStartElement:
   215  		el := new(Element)
   216  		if len(bx.stack) == 0 {
   217  			bx.Children = append(bx.Children, el)
   218  		} else {
   219  			n := len(bx.stack)
   220  			var p *Element
   221  			p, bx.stack = bx.stack[n-1], bx.stack[:n-1]
   222  			p.Children = append(p.Children, el)
   223  			bx.stack = append(bx.stack, p)
   224  		}
   225  		bx.stack = append(bx.stack, el)
   226  		return el, nil
   227  	case ResXMLEndElement:
   228  		n := len(bx.stack)
   229  		var el *Element
   230  		el, bx.stack = bx.stack[n-1], bx.stack[:n-1]
   231  		if el.end != nil {
   232  			return nil, fmt.Errorf("element end already exists")
   233  		}
   234  		el.end = new(ElementEnd)
   235  		return el.end, nil
   236  	case ResXMLCharData: // TODO
   237  		cdt := new(CharData)
   238  		el := bx.stack[len(bx.stack)-1]
   239  		if el.head == nil {
   240  			el.head = cdt
   241  		} else if el.tail == nil {
   242  			el.tail = cdt
   243  		} else {
   244  			return nil, fmt.Errorf("element head and tail already contain chardata")
   245  		}
   246  		return cdt, nil
   247  	default:
   248  		return nil, fmt.Errorf("unexpected type %s", t)
   249  	}
   250  }
   251  
   252  func (bx *XML) MarshalBinary() ([]byte, error) {
   253  	var (
   254  		bin, b []byte
   255  		err    error
   256  	)
   257  	b, err = bx.chunkHeader.MarshalBinary()
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  	bin = append(bin, b...)
   262  
   263  	b, err = bx.Pool.MarshalBinary()
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	bin = append(bin, b...)
   268  
   269  	b, err = bx.Map.MarshalBinary()
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	bin = append(bin, b...)
   274  
   275  	b, err = bx.Namespace.MarshalBinary()
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	bin = append(bin, b...)
   280  
   281  	for _, child := range bx.Children {
   282  		if err := marshalRecurse(child, &bin); err != nil {
   283  			return nil, err
   284  		}
   285  	}
   286  
   287  	b, err = bx.Namespace.end.MarshalBinary()
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	bin = append(bin, b...)
   292  
   293  	return bin, nil
   294  }
   295  
   296  func marshalRecurse(el *Element, bin *[]byte) error {
   297  	b, err := el.MarshalBinary()
   298  	if err != nil {
   299  		return err
   300  	}
   301  	*bin = append(*bin, b...)
   302  
   303  	if el.head != nil {
   304  		b, err := el.head.MarshalBinary()
   305  		if err != nil {
   306  			return err
   307  		}
   308  		*bin = append(*bin, b...)
   309  	}
   310  
   311  	for _, child := range el.Children {
   312  		if err := marshalRecurse(child, bin); err != nil {
   313  			return err
   314  		}
   315  	}
   316  
   317  	b, err = el.end.MarshalBinary()
   318  	if err != nil {
   319  		return err
   320  	}
   321  	*bin = append(*bin, b...)
   322  
   323  	return nil
   324  }
   325  
   326  func (bx *XML) iterElements() <-chan *Element {
   327  	ch := make(chan *Element, 1)
   328  	go func() {
   329  		for _, el := range bx.Children {
   330  			iterElementsRecurse(el, ch)
   331  		}
   332  		close(ch)
   333  	}()
   334  	return ch
   335  }
   336  
   337  func iterElementsRecurse(el *Element, ch chan *Element) {
   338  	ch <- el
   339  	for _, e := range el.Children {
   340  		iterElementsRecurse(e, ch)
   341  	}
   342  }