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 }