bplist_generator.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package plist
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "time"
  8. "unicode/utf16"
  9. )
  10. func bplistMinimumIntSize(n uint64) int {
  11. switch {
  12. case n <= uint64(0xff):
  13. return 1
  14. case n <= uint64(0xffff):
  15. return 2
  16. case n <= uint64(0xffffffff):
  17. return 4
  18. default:
  19. return 8
  20. }
  21. }
  22. func bplistValueShouldUnique(pval cfValue) bool {
  23. switch pval.(type) {
  24. case cfString, *cfNumber, *cfReal, cfDate, cfData:
  25. return true
  26. }
  27. return false
  28. }
  29. type bplistGenerator struct {
  30. writer *countedWriter
  31. objmap map[interface{}]uint64 // maps pValue.hash()es to object locations
  32. objtable []cfValue
  33. trailer bplistTrailer
  34. }
  35. func (p *bplistGenerator) flattenPlistValue(pval cfValue) {
  36. key := pval.hash()
  37. if bplistValueShouldUnique(pval) {
  38. if _, ok := p.objmap[key]; ok {
  39. return
  40. }
  41. }
  42. p.objmap[key] = uint64(len(p.objtable))
  43. p.objtable = append(p.objtable, pval)
  44. switch pval := pval.(type) {
  45. case *cfDictionary:
  46. pval.sort()
  47. for _, k := range pval.keys {
  48. p.flattenPlistValue(cfString(k))
  49. }
  50. for _, v := range pval.values {
  51. p.flattenPlistValue(v)
  52. }
  53. case *cfArray:
  54. for _, v := range pval.values {
  55. p.flattenPlistValue(v)
  56. }
  57. }
  58. }
  59. func (p *bplistGenerator) indexForPlistValue(pval cfValue) (uint64, bool) {
  60. v, ok := p.objmap[pval.hash()]
  61. return v, ok
  62. }
  63. func (p *bplistGenerator) generateDocument(root cfValue) {
  64. p.objtable = make([]cfValue, 0, 16)
  65. p.objmap = make(map[interface{}]uint64)
  66. p.flattenPlistValue(root)
  67. p.trailer.NumObjects = uint64(len(p.objtable))
  68. p.trailer.ObjectRefSize = uint8(bplistMinimumIntSize(p.trailer.NumObjects))
  69. p.writer.Write([]byte("bplist00"))
  70. offtable := make([]uint64, p.trailer.NumObjects)
  71. for i, pval := range p.objtable {
  72. offtable[i] = uint64(p.writer.BytesWritten())
  73. p.writePlistValue(pval)
  74. }
  75. p.trailer.OffsetIntSize = uint8(bplistMinimumIntSize(uint64(p.writer.BytesWritten())))
  76. p.trailer.TopObject = p.objmap[root.hash()]
  77. p.trailer.OffsetTableOffset = uint64(p.writer.BytesWritten())
  78. for _, offset := range offtable {
  79. p.writeSizedInt(offset, int(p.trailer.OffsetIntSize))
  80. }
  81. binary.Write(p.writer, binary.BigEndian, p.trailer)
  82. }
  83. func (p *bplistGenerator) writePlistValue(pval cfValue) {
  84. if pval == nil {
  85. return
  86. }
  87. switch pval := pval.(type) {
  88. case *cfDictionary:
  89. p.writeDictionaryTag(pval)
  90. case *cfArray:
  91. p.writeArrayTag(pval.values)
  92. case cfString:
  93. p.writeStringTag(string(pval))
  94. case *cfNumber:
  95. p.writeIntTag(pval.signed, pval.value)
  96. case *cfReal:
  97. if pval.wide {
  98. p.writeRealTag(pval.value, 64)
  99. } else {
  100. p.writeRealTag(pval.value, 32)
  101. }
  102. case cfBoolean:
  103. p.writeBoolTag(bool(pval))
  104. case cfData:
  105. p.writeDataTag([]byte(pval))
  106. case cfDate:
  107. p.writeDateTag(time.Time(pval))
  108. case cfUID:
  109. p.writeUIDTag(UID(pval))
  110. default:
  111. panic(fmt.Errorf("unknown plist type %t", pval))
  112. }
  113. }
  114. func (p *bplistGenerator) writeSizedInt(n uint64, nbytes int) {
  115. var val interface{}
  116. switch nbytes {
  117. case 1:
  118. val = uint8(n)
  119. case 2:
  120. val = uint16(n)
  121. case 4:
  122. val = uint32(n)
  123. case 8:
  124. val = n
  125. default:
  126. panic(errors.New("illegal integer size"))
  127. }
  128. binary.Write(p.writer, binary.BigEndian, val)
  129. }
  130. func (p *bplistGenerator) writeBoolTag(v bool) {
  131. tag := uint8(bpTagBoolFalse)
  132. if v {
  133. tag = bpTagBoolTrue
  134. }
  135. binary.Write(p.writer, binary.BigEndian, tag)
  136. }
  137. func (p *bplistGenerator) writeIntTag(signed bool, n uint64) {
  138. var tag uint8
  139. var val interface{}
  140. switch {
  141. case n <= uint64(0xff):
  142. val = uint8(n)
  143. tag = bpTagInteger | 0x0
  144. case n <= uint64(0xffff):
  145. val = uint16(n)
  146. tag = bpTagInteger | 0x1
  147. case n <= uint64(0xffffffff):
  148. val = uint32(n)
  149. tag = bpTagInteger | 0x2
  150. case n > uint64(0x7fffffffffffffff) && !signed:
  151. // 64-bit values are always *signed* in format 00.
  152. // Any unsigned value that doesn't intersect with the signed
  153. // range must be sign-extended and stored as a SInt128
  154. val = n
  155. tag = bpTagInteger | 0x4
  156. default:
  157. val = n
  158. tag = bpTagInteger | 0x3
  159. }
  160. binary.Write(p.writer, binary.BigEndian, tag)
  161. if tag&0xF == 0x4 {
  162. // SInt128; in the absence of true 128-bit integers in Go,
  163. // we'll just fake the top half. We only got here because
  164. // we had an unsigned 64-bit int that didn't fit,
  165. // so sign extend it with zeroes.
  166. binary.Write(p.writer, binary.BigEndian, uint64(0))
  167. }
  168. binary.Write(p.writer, binary.BigEndian, val)
  169. }
  170. func (p *bplistGenerator) writeUIDTag(u UID) {
  171. nbytes := bplistMinimumIntSize(uint64(u))
  172. tag := uint8(bpTagUID | (nbytes - 1))
  173. binary.Write(p.writer, binary.BigEndian, tag)
  174. p.writeSizedInt(uint64(u), nbytes)
  175. }
  176. func (p *bplistGenerator) writeRealTag(n float64, bits int) {
  177. var tag uint8 = bpTagReal | 0x3
  178. var val interface{} = n
  179. if bits == 32 {
  180. val = float32(n)
  181. tag = bpTagReal | 0x2
  182. }
  183. binary.Write(p.writer, binary.BigEndian, tag)
  184. binary.Write(p.writer, binary.BigEndian, val)
  185. }
  186. func (p *bplistGenerator) writeDateTag(t time.Time) {
  187. tag := uint8(bpTagDate) | 0x3
  188. val := float64(t.In(time.UTC).UnixNano()) / float64(time.Second)
  189. val -= 978307200 // Adjust to Apple Epoch
  190. binary.Write(p.writer, binary.BigEndian, tag)
  191. binary.Write(p.writer, binary.BigEndian, val)
  192. }
  193. func (p *bplistGenerator) writeCountedTag(tag uint8, count uint64) {
  194. marker := tag
  195. if count >= 0xF {
  196. marker |= 0xF
  197. } else {
  198. marker |= uint8(count)
  199. }
  200. binary.Write(p.writer, binary.BigEndian, marker)
  201. if count >= 0xF {
  202. p.writeIntTag(false, count)
  203. }
  204. }
  205. func (p *bplistGenerator) writeDataTag(data []byte) {
  206. p.writeCountedTag(bpTagData, uint64(len(data)))
  207. binary.Write(p.writer, binary.BigEndian, data)
  208. }
  209. func (p *bplistGenerator) writeStringTag(str string) {
  210. for _, r := range str {
  211. if r > 0x7F {
  212. utf16Runes := utf16.Encode([]rune(str))
  213. p.writeCountedTag(bpTagUTF16String, uint64(len(utf16Runes)))
  214. binary.Write(p.writer, binary.BigEndian, utf16Runes)
  215. return
  216. }
  217. }
  218. p.writeCountedTag(bpTagASCIIString, uint64(len(str)))
  219. binary.Write(p.writer, binary.BigEndian, []byte(str))
  220. }
  221. func (p *bplistGenerator) writeDictionaryTag(dict *cfDictionary) {
  222. // assumption: sorted already; flattenPlistValue did this.
  223. cnt := len(dict.keys)
  224. p.writeCountedTag(bpTagDictionary, uint64(cnt))
  225. vals := make([]uint64, cnt*2)
  226. for i, k := range dict.keys {
  227. // invariant: keys have already been "uniqued" (as PStrings)
  228. keyIdx, ok := p.objmap[cfString(k).hash()]
  229. if !ok {
  230. panic(errors.New("failed to find key " + k + " in object map during serialization"))
  231. }
  232. vals[i] = keyIdx
  233. }
  234. for i, v := range dict.values {
  235. // invariant: values have already been "uniqued"
  236. objIdx, ok := p.indexForPlistValue(v)
  237. if !ok {
  238. panic(errors.New("failed to find value in object map during serialization"))
  239. }
  240. vals[i+cnt] = objIdx
  241. }
  242. for _, v := range vals {
  243. p.writeSizedInt(v, int(p.trailer.ObjectRefSize))
  244. }
  245. }
  246. func (p *bplistGenerator) writeArrayTag(arr []cfValue) {
  247. p.writeCountedTag(bpTagArray, uint64(len(arr)))
  248. for _, v := range arr {
  249. objIdx, ok := p.indexForPlistValue(v)
  250. if !ok {
  251. panic(errors.New("failed to find value in object map during serialization"))
  252. }
  253. p.writeSizedInt(objIdx, int(p.trailer.ObjectRefSize))
  254. }
  255. }
  256. func (p *bplistGenerator) Indent(i string) {
  257. // There's nothing to indent.
  258. }
  259. func newBplistGenerator(w io.Writer) *bplistGenerator {
  260. return &bplistGenerator{
  261. writer: &countedWriter{Writer: mustWriter{w}},
  262. }
  263. }