CFPropertyList 2.0.14 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'stringio'
4
+
3
5
  module CFPropertyList
4
6
  # Binary PList parser class
5
7
  class Binary
@@ -16,7 +18,7 @@ module CFPropertyList
16
18
  @offsets = []
17
19
 
18
20
  fd = nil
19
- if(opts.has_key?(:file)) then
21
+ if(opts.has_key?(:file))
20
22
  fd = File.open(opts[:file],"rb")
21
23
  file = opts[:file]
22
24
  else
@@ -38,17 +40,20 @@ module CFPropertyList
38
40
  @count_objects = number_of_objects
39
41
 
40
42
  # decode offset table
41
- formats = ["","C*","n*","(H6)*","N*"]
42
- @offsets = coded_offset_table.unpack(formats[offset_size])
43
- if(offset_size == 3) then
44
- 0.upto(@offsets.size-1) { |i| @offsets[i] = @offsets[i].to_i(16) }
43
+ if(offset_size != 3)
44
+ formats = ["","C*","n*","","N*"]
45
+ @offsets = coded_offset_table.unpack(formats[offset_size])
46
+ else
47
+ @offsets = coded_offset_table.unpack("C*").each_slice(3).map {
48
+ |x,y,z| (x << 16) | (y << 8) | z
49
+ }
45
50
  end
46
51
 
47
52
  @object_ref_size = object_ref_size
48
53
  val = read_binary_object_at(file,fd,top_object)
49
54
 
50
55
  fd.close
51
- return val
56
+ val
52
57
  end
53
58
 
54
59
 
@@ -60,19 +65,21 @@ module CFPropertyList
60
65
 
61
66
  @written_object_count = 0
62
67
  @object_table = []
63
- @object_ref_size = 0
64
68
 
65
69
  @offsets = []
66
70
 
67
71
  binary_str = "bplist00"
68
72
 
69
73
  @object_refs = count_object_refs(opts[:root])
70
- @object_ref_size = Binary.bytes_needed(@object_refs)
71
74
 
72
75
  opts[:root].to_binary(self)
73
76
 
74
77
  next_offset = 8
75
- offsets = @object_table.collect { |object| offset = next_offset; next_offset += object.bytesize; offset }
78
+ offsets = @object_table.map do |object|
79
+ offset = next_offset
80
+ next_offset += object.bytesize
81
+ offset
82
+ end
76
83
  binary_str << @object_table.join
77
84
 
78
85
  table_offset = next_offset
@@ -89,60 +96,54 @@ module CFPropertyList
89
96
  end
90
97
  end
91
98
 
92
- binary_str << [offset_size, @object_ref_size].pack("x6CC")
99
+ binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
93
100
  binary_str << [@object_table.size].pack("x4N")
94
101
  binary_str << [0].pack("x4N")
95
102
  binary_str << [table_offset].pack("x4N")
96
103
 
97
- return binary_str
104
+ binary_str
105
+ end
106
+
107
+ def object_ref_size object_refs
108
+ Binary.bytes_needed(object_refs)
98
109
  end
99
110
 
100
111
  # read a „null” type (i.e. null byte, marker byte, bool value)
101
112
  def read_binary_null_type(length)
102
113
  case length
103
- when 0 then return 0 # null byte
104
- when 8 then return CFBoolean.new(false)
105
- when 9 then return CFBoolean.new(true)
106
- when 15 then return 15 # fill type
114
+ when 0 then 0 # null byte
115
+ when 8 then CFBoolean.new(false)
116
+ when 9 then CFBoolean.new(true)
117
+ when 15 then 15 # fill type
118
+ else
119
+ raise CFFormatError.new("unknown null type: #{length}")
107
120
  end
108
-
109
- raise CFFormatError.new("unknown null type: #{length}")
110
121
  end
111
122
  protected :read_binary_null_type
112
123
 
113
124
  # read a binary int value
114
125
  def read_binary_int(fname,fd,length)
115
- raise CFFormatError.new("Integer greater than 8 bytes: #{length}") if length > 3
126
+ if length > 4
127
+ raise CFFormatError.new("Integer greater than 16 bytes: #{length}")
128
+ end
116
129
 
117
130
  nbytes = 1 << length
118
131
 
119
- val = nil
120
132
  buff = fd.read(nbytes)
121
133
 
122
- case length
123
- when 0 then
124
- val = buff.unpack("C")
125
- val = val[0]
126
- when 1 then
127
- val = buff.unpack("n")
128
- val = val[0]
129
- when 2 then
130
- val = buff.unpack("N")
131
- val = val[0]
132
- when 3
133
- hiword,loword = buff.unpack("NN")
134
- if (hiword & 0x80000000) != 0
135
- # 8 byte integers are always signed, and are negative when bit 63 is
136
- # set. Decoding into either a Fixnum or Bignum is tricky, however,
137
- # because the size of a Fixnum varies among systems, and Ruby
138
- # doesn't consider the number to be negative, and won't sign extend.
139
- val = -(2**63 - ((hiword & 0x7fffffff) << 32 | loword))
140
- else
141
- val = hiword << 32 | loword
134
+ CFInteger.new(
135
+ case length
136
+ when 0 then buff.unpack("C")[0]
137
+ when 1 then buff.unpack("n")[0]
138
+ when 2 then buff.unpack("N")[0]
139
+ # 8 byte integers are always signed
140
+ when 3 then buff.unpack("q>")[0]
141
+ # 16 byte integers are used to represent unsigned 8 byte integers
142
+ # where the unsigned value is stored in the lower 8 bytes and the
143
+ # upper 8 bytes are unused.
144
+ when 4 then buff.unpack("Q>Q>")[1]
142
145
  end
143
- end
144
-
145
- return CFInteger.new(val);
146
+ )
146
147
  end
147
148
  protected :read_binary_int
148
149
 
@@ -151,23 +152,22 @@ module CFPropertyList
151
152
  raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
152
153
 
153
154
  nbytes = 1 << length
154
- val = nil
155
155
  buff = fd.read(nbytes)
156
156
 
157
- case length
158
- when 0 then # 1 byte float? must be an error
159
- raise CFFormatError.new("got #{length+1} byte float, must be an error!")
160
- when 1 then # 2 byte float? must be an error
161
- raise CFFormatError.new("got #{length+1} byte float, must be an error!")
162
- when 2 then
163
- val = buff.reverse.unpack("f")
164
- val = val[0]
165
- when 3 then
166
- val = buff.reverse.unpack("d")
167
- val = val[0]
168
- end
169
-
170
- return CFReal.new(val)
157
+ CFReal.new(
158
+ case length
159
+ when 0 # 1 byte float? must be an error
160
+ raise CFFormatError.new("got #{length+1} byte float, must be an error!")
161
+ when 1 # 2 byte float? must be an error
162
+ raise CFFormatError.new("got #{length+1} byte float, must be an error!")
163
+ when 2 then
164
+ buff.reverse.unpack("e")[0]
165
+ when 3 then
166
+ buff.reverse.unpack("E")[0]
167
+ else
168
+ fail "unexpected length: #{length}"
169
+ end
170
+ )
171
171
  end
172
172
  protected :read_binary_real
173
173
 
@@ -176,48 +176,46 @@ module CFPropertyList
176
176
  raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
177
177
 
178
178
  nbytes = 1 << length
179
- val = nil
180
179
  buff = fd.read(nbytes)
181
180
 
182
- case length
183
- when 0 then # 1 byte CFDate is an error
184
- raise CFFormatError.new("#{length+1} byte CFDate, error")
185
- when 1 then # 2 byte CFDate is an error
186
- raise CFFormatError.new("#{length+1} byte CFDate, error")
187
- when 2 then
188
- val = buff.reverse.unpack("f")
189
- val = val[0]
190
- when 3 then
191
- val = buff.reverse.unpack("d")
192
- val = val[0]
193
- end
194
-
195
- return CFDate.new(val,CFDate::TIMESTAMP_APPLE)
181
+ CFDate.new(
182
+ case length
183
+ when 0 then # 1 byte CFDate is an error
184
+ raise CFFormatError.new("#{length+1} byte CFDate, error")
185
+ when 1 then # 2 byte CFDate is an error
186
+ raise CFFormatError.new("#{length+1} byte CFDate, error")
187
+ when 2 then
188
+ buff.reverse.unpack("e")[0]
189
+ when 3 then
190
+ buff.reverse.unpack("E")[0]
191
+ end,
192
+ CFDate::TIMESTAMP_APPLE
193
+ )
196
194
  end
197
195
  protected :read_binary_date
198
196
 
199
197
  # Read a binary data value
200
198
  def read_binary_data(fname,fd,length)
201
- buff = "";
202
- buff = fd.read(length) if length > 0
203
- return CFData.new(buff,CFData::DATA_RAW)
199
+ CFData.new(read_fd(fd, length), CFData::DATA_RAW)
204
200
  end
205
201
  protected :read_binary_data
206
202
 
203
+ def read_fd fd, length
204
+ length > 0 ? fd.read(length) : ""
205
+ end
206
+
207
207
  # Read a binary string value
208
208
  def read_binary_string(fname,fd,length)
209
- buff = ""
210
- buff = fd.read(length) if length > 0
211
-
209
+ buff = read_fd fd, length
212
210
  @unique_table[buff] = true unless @unique_table.has_key?(buff)
213
- return CFString.new(buff)
211
+ CFString.new(buff)
214
212
  end
215
213
  protected :read_binary_string
216
214
 
217
215
  # Convert the given string from one charset to another
218
216
  def Binary.charset_convert(str,from,to="UTF-8")
219
- return str.clone.force_encoding(from).encode(to) if str.respond_to?("encode")
220
- return Iconv.conv(to,from,str)
217
+ return str.dup.force_encoding(from).encode(to) if str.respond_to?("encode")
218
+ Iconv.conv(to,from,str)
221
219
  end
222
220
 
223
221
  # Count characters considering character set
@@ -228,7 +226,7 @@ module CFPropertyList
228
226
  utf8_str = Iconv.conv("UTF-8",charset,str)
229
227
  size = utf8_str.scan(/./mu).size
230
228
  end
231
-
229
+
232
230
  # UTF-16 code units in the range D800-DBFF are the beginning of
233
231
  # a surrogate pair, and count as one additional character for
234
232
  # length calculation.
@@ -239,8 +237,8 @@ module CFPropertyList
239
237
  str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
240
238
  end
241
239
  end
242
-
243
- return size
240
+
241
+ size
244
242
  end
245
243
 
246
244
  # Read a unicode string value, coded as UTF-16BE
@@ -251,18 +249,28 @@ module CFPropertyList
251
249
  buff = fd.read(2*length)
252
250
 
253
251
  @unique_table[buff] = true unless @unique_table.has_key?(buff)
254
- return CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
252
+ CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
255
253
  end
256
254
  protected :read_binary_unicode_string
257
255
 
256
+ def unpack_with_size(nbytes, buff)
257
+ format = ["C*", "n*", "N*", "N*"][nbytes - 1];
258
+
259
+ if nbytes == 3
260
+ buff = "\0" + buff.scan(/.{1,3}/).join("\0")
261
+ end
262
+
263
+ return buff.unpack(format)
264
+ end
265
+
258
266
  # Read an binary array value, including contained objects
259
267
  def read_binary_array(fname,fd,length)
260
268
  ary = []
261
269
 
262
270
  # first: read object refs
263
- if(length != 0) then
271
+ if(length != 0)
264
272
  buff = fd.read(length * @object_ref_size)
265
- objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
273
+ objects = unpack_with_size(@object_ref_size, buff) #buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
266
274
 
267
275
  # now: read objects
268
276
  0.upto(length-1) do |i|
@@ -271,7 +279,7 @@ module CFPropertyList
271
279
  end
272
280
  end
273
281
 
274
- return CFArray.new(ary)
282
+ CFArray.new(ary)
275
283
  end
276
284
  protected :read_binary_array
277
285
 
@@ -282,11 +290,11 @@ module CFPropertyList
282
290
  # first: read keys
283
291
  if(length != 0) then
284
292
  buff = fd.read(length * @object_ref_size)
285
- keys = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
293
+ keys = unpack_with_size(@object_ref_size, buff)
286
294
 
287
295
  # second: read object refs
288
296
  buff = fd.read(length * @object_ref_size)
289
- objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
297
+ objects = unpack_with_size(@object_ref_size, buff)
290
298
 
291
299
  # read real keys and objects
292
300
  0.upto(length-1) do |i|
@@ -296,17 +304,18 @@ module CFPropertyList
296
304
  end
297
305
  end
298
306
 
299
- return CFDictionary.new(dict)
307
+ CFDictionary.new(dict)
300
308
  end
301
309
  protected :read_binary_dict
302
310
 
303
- # Read an object type byte, decode it and delegate to the correct reader function
311
+ # Read an object type byte, decode it and delegate to the correct
312
+ # reader function
304
313
  def read_binary_object(fname,fd)
305
314
  # first: read the marker byte
306
315
  buff = fd.read(1)
307
316
 
308
317
  object_length = buff.unpack("C*")
309
- object_length = object_length[0] & 0xF
318
+ object_length = object_length[0] & 0xF
310
319
 
311
320
  buff = buff.unpack("H*")
312
321
  object_type = buff[0][0].chr
@@ -316,29 +325,28 @@ module CFPropertyList
316
325
  object_length = object_length.value
317
326
  end
318
327
 
319
- retval = nil
320
328
  case object_type
321
- when '0' then # null, false, true, fillbyte
322
- retval = read_binary_null_type(object_length)
323
- when '1' then # integer
324
- retval = read_binary_int(fname,fd,object_length)
325
- when '2' then # real
326
- retval = read_binary_real(fname,fd,object_length)
327
- when '3' then # date
328
- retval = read_binary_date(fname,fd,object_length)
329
- when '4' then # data
330
- retval = read_binary_data(fname,fd,object_length)
331
- when '5' then # byte string, usually utf8 encoded
332
- retval = read_binary_string(fname,fd,object_length)
333
- when '6' then # unicode string (utf16be)
334
- retval = read_binary_unicode_string(fname,fd,object_length)
335
- when 'a' then # array
336
- retval = read_binary_array(fname,fd,object_length)
337
- when 'd' then # dictionary
338
- retval = read_binary_dict(fname,fd,object_length)
329
+ when '0' # null, false, true, fillbyte
330
+ read_binary_null_type(object_length)
331
+ when '1' # integer
332
+ read_binary_int(fname,fd,object_length)
333
+ when '2' # real
334
+ read_binary_real(fname,fd,object_length)
335
+ when '3' # date
336
+ read_binary_date(fname,fd,object_length)
337
+ when '4' # data
338
+ read_binary_data(fname,fd,object_length)
339
+ when '5' # byte string, usually utf8 encoded
340
+ read_binary_string(fname,fd,object_length)
341
+ when '6' # unicode string (utf16be)
342
+ read_binary_unicode_string(fname,fd,object_length)
343
+ when '8'
344
+ CFUid.new(read_binary_int(fname, fd, object_length).value)
345
+ when 'a' # array
346
+ read_binary_array(fname,fd,object_length)
347
+ when 'd' # dictionary
348
+ read_binary_dict(fname,fd,object_length)
339
349
  end
340
-
341
- return retval
342
350
  end
343
351
  protected :read_binary_object
344
352
 
@@ -346,36 +354,30 @@ module CFPropertyList
346
354
  def read_binary_object_at(fname,fd,pos)
347
355
  position = @offsets[pos]
348
356
  fd.seek(position,IO::SEEK_SET)
349
- return read_binary_object(fname,fd)
357
+ read_binary_object(fname,fd)
350
358
  end
351
359
  protected :read_binary_object_at
352
360
 
353
361
  # pack an +int+ of +nbytes+ with size
354
362
  def Binary.pack_it_with_size(nbytes,int)
355
363
  case nbytes
356
- when 1
357
- return [int].pack('c')
358
- when 2
359
- return [int].pack('n')
360
- when 4
361
- return [int].pack('N')
364
+ when 1 then [int].pack('c')
365
+ when 2 then [int].pack('n')
366
+ when 4 then [int].pack('N')
362
367
  when 8
363
- return [int >> 32, int & 0xFFFFFFFF].pack('NN')
368
+ [int >> 32, int & 0xFFFFFFFF].pack('NN')
364
369
  else
365
370
  raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
366
371
  end
367
372
  end
368
-
373
+
369
374
  def Binary.pack_int_array_with_size(nbytes, array)
370
375
  case nbytes
371
- when 1
372
- array.pack('C*')
373
- when 2
374
- array.pack('n*')
375
- when 4
376
- array.pack('N*')
376
+ when 1 then array.pack('C*')
377
+ when 2 then array.pack('n*')
378
+ when 4 then array.pack('N*')
377
379
  when 8
378
- array.collect { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
380
+ array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
379
381
  else
380
382
  raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
381
383
  end
@@ -383,41 +385,36 @@ module CFPropertyList
383
385
 
384
386
  # calculate how many bytes are needed to save +count+
385
387
  def Binary.bytes_needed(count)
386
- if count < 2**8
387
- nbytes = 1
388
- elsif count < 2**16
389
- nbytes = 2
390
- elsif count < 2**32
391
- nbytes = 4
392
- elsif count < 2**64
393
- nbytes = 8
388
+ case
389
+ when count < 2**8 then 1
390
+ when count < 2**16 then 2
391
+ when count < 2**32 then 4
392
+ when count < 2**64 then 8
394
393
  else
395
394
  raise CFFormatError.new("Data size too large: #{count}")
396
395
  end
397
-
398
- return nbytes
399
396
  end
400
397
 
401
398
  # Create a type byte for binary format as defined by apple
402
399
  def Binary.type_bytes(type, length)
403
400
  if length < 15
404
- return [(type << 4) | length].pack('C')
401
+ [(type << 4) | length].pack('C')
405
402
  else
406
403
  bytes = [(type << 4) | 0xF]
407
404
  if length <= 0xFF
408
- return bytes.push(0x10, length).pack('CCC') # 1 byte length
405
+ bytes.push(0x10, length).pack('CCC') # 1 byte length
409
406
  elsif length <= 0xFFFF
410
- return bytes.push(0x11, length).pack('CCn') # 2 byte length
407
+ bytes.push(0x11, length).pack('CCn') # 2 byte length
411
408
  elsif length <= 0xFFFFFFFF
412
- return bytes.push(0x12, length).pack('CCN') # 4 byte length
409
+ bytes.push(0x12, length).pack('CCN') # 4 byte length
413
410
  elsif length <= 0x7FFFFFFFFFFFFFFF
414
- return bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
411
+ bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
415
412
  else
416
413
  raise CFFormatError.new("Integer too large: #{int}")
417
414
  end
418
415
  end
419
416
  end
420
-
417
+
421
418
  def count_object_refs(object)
422
419
  case object
423
420
  when CFArray
@@ -443,140 +440,136 @@ module CFPropertyList
443
440
 
444
441
  def Binary.ascii_string?(str)
445
442
  if str.respond_to?(:ascii_only?)
446
- return str.ascii_only?
443
+ str.ascii_only?
447
444
  else
448
- return str.scan(/[\x80-\xFF]/mn).size == 0
445
+ str !~ /[\x80-\xFF]/mn
449
446
  end
450
447
  end
451
-
448
+
452
449
  # Uniques and transforms a string value to binary format and adds it to the object table
453
450
  def string_to_binary(val)
454
- saved_object_count = -1
455
-
456
- unless(@unique_table.has_key?(val)) then
457
- saved_object_count = @written_object_count
458
- @written_object_count += 1
459
-
460
- @unique_table[val] = saved_object_count
461
- utf16 = !Binary.ascii_string?(val)
451
+ val = val.to_s
462
452
 
463
- utf8_strlen = 0
464
- if(utf16) then
465
- utf8_strlen = Binary.charset_strlen(val, "UTF-8")
453
+ @unique_table[val] ||= begin
454
+ if !Binary.ascii_string?(val)
466
455
  val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
467
456
  bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
468
457
 
469
458
  val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
470
- @object_table[saved_object_count] = bdata + val
459
+ @object_table[@written_object_count] = bdata << val
471
460
  else
472
- utf8_strlen = val.bytesize
473
461
  bdata = Binary.type_bytes(0b0101,val.bytesize)
474
- @object_table[saved_object_count] = bdata + val
462
+ @object_table[@written_object_count] = bdata << val
475
463
  end
476
- else
477
- saved_object_count = @unique_table[val]
478
- end
479
464
 
480
- return saved_object_count
465
+ @written_object_count += 1
466
+ @written_object_count - 1
467
+ end
481
468
  end
482
469
 
483
470
  # Codes an integer to binary format
484
471
  def int_to_binary(value)
472
+ # Note: nbytes is actually an exponent. number of bytes = 2**nbytes.
485
473
  nbytes = 0
486
- nbytes = 1 if value > 0xFF # 1 byte integer
487
- nbytes += 1 if value > 0xFFFF # 4 byte integer
488
- nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
489
- nbytes = 3 if value < 0 # 8 byte integer, since signed
490
-
491
- bdata = Binary.type_bytes(0b0001, nbytes)
492
- buff = ""
493
-
494
- if(nbytes < 3) then
495
- fmt = "N"
496
-
497
- if(nbytes == 0) then
498
- fmt = "C"
499
- elsif(nbytes == 1)
500
- fmt = "n"
474
+ nbytes = 1 if value > 0xFF # 1 byte unsigned integer
475
+ nbytes += 1 if value > 0xFFFF # 4 byte unsigned integer
476
+ nbytes += 1 if value > 0xFFFFFFFF # 8 byte unsigned integer
477
+ nbytes += 1 if value > 0x7FFFFFFFFFFFFFFF # 8 byte unsigned integer, stored in lower half of 16 bytes
478
+ nbytes = 3 if value < 0 # signed integers always stored in 8 bytes
479
+
480
+ Binary.type_bytes(0b0001, nbytes) <<
481
+ if nbytes < 4
482
+ [value].pack(["C", "n", "N", "q>"][nbytes])
483
+ else # nbytes == 4
484
+ [0,value].pack("Q>Q>")
501
485
  end
502
-
503
- buff = [value].pack(fmt)
504
- else
505
- # 64 bit signed integer; we need the higher and the lower 32 bit of the value
506
- high_word = value >> 32
507
- low_word = value & 0xFFFFFFFF
508
- buff = [high_word,low_word].pack("NN")
509
- end
510
-
511
- return bdata + buff
512
486
  end
513
487
 
514
488
  # Codes a real value to binary format
515
489
  def real_to_binary(val)
516
- bdata = Binary.type_bytes(0b0010,3)
517
- buff = [val].pack("d")
518
- return bdata + buff.reverse
490
+ Binary.type_bytes(0b0010,3) << [val].pack("E").reverse
519
491
  end
520
492
 
521
493
  # Converts a numeric value to binary and adds it to the object table
522
494
  def num_to_binary(value)
523
- saved_object_count = @written_object_count
495
+ @object_table[@written_object_count] =
496
+ if value.is_a?(CFInteger)
497
+ int_to_binary(value.value)
498
+ else
499
+ real_to_binary(value.value)
500
+ end
501
+
524
502
  @written_object_count += 1
503
+ @written_object_count - 1
504
+ end
525
505
 
526
- val = ""
527
- if(value.is_a?(CFInteger)) then
528
- val = int_to_binary(value.value)
529
- else
530
- val = real_to_binary(value.value)
531
- end
506
+ def uid_to_binary(value)
507
+ nbytes = 0
508
+ nbytes = 1 if value > 0xFF # 1 byte integer
509
+ nbytes += 1 if value > 0xFFFF # 4 byte integer
510
+ nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
511
+ nbytes = 3 if value < 0 # 8 byte integer, since signed
512
+
513
+ @object_table[@written_object_count] = Binary.type_bytes(0b1000, nbytes) <<
514
+ if nbytes < 3
515
+ [value].pack(
516
+ if nbytes == 0 then "C"
517
+ elsif nbytes == 1 then "n"
518
+ else "N"
519
+ end
520
+ )
521
+ else
522
+ # 64 bit signed integer; we need the higher and the lower 32 bit of the value
523
+ high_word = value >> 32
524
+ low_word = value & 0xFFFFFFFF
525
+ [high_word,low_word].pack("NN")
526
+ end
532
527
 
533
- @object_table[saved_object_count] = val
534
- return saved_object_count
528
+ @written_object_count += 1
529
+ @written_object_count - 1
535
530
  end
536
531
 
537
532
  # Convert date value (apple format) to binary and adds it to the object table
538
533
  def date_to_binary(val)
539
- saved_object_count = @written_object_count
540
- @written_object_count += 1
541
-
542
534
  val = val.getutc.to_f - CFDate::DATE_DIFF_APPLE_UNIX # CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT
543
535
 
544
- bdata = Binary.type_bytes(0b0011, 3)
545
- @object_table[saved_object_count] = bdata + [val].pack("d").reverse
536
+ @object_table[@written_object_count] =
537
+ (Binary.type_bytes(0b0011, 3) << [val].pack("E").reverse)
546
538
 
547
- return saved_object_count
539
+ @written_object_count += 1
540
+ @written_object_count - 1
548
541
  end
549
542
 
550
543
  # Convert a bool value to binary and add it to the object table
551
544
  def bool_to_binary(val)
552
- saved_object_count = @written_object_count
553
- @written_object_count += 1
554
545
 
555
- @object_table[saved_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
556
- return saved_object_count
546
+ @object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
547
+ @written_object_count += 1
548
+ @written_object_count - 1
557
549
  end
558
550
 
559
551
  # Convert data value to binary format and add it to the object table
560
552
  def data_to_binary(val)
561
- saved_object_count = @written_object_count
562
- @written_object_count += 1
563
-
564
- bdata = Binary.type_bytes(0b0100, val.bytesize)
565
- @object_table[saved_object_count] = bdata + val
553
+ @object_table[@written_object_count] =
554
+ (Binary.type_bytes(0b0100, val.bytesize) << val)
566
555
 
567
- return saved_object_count
556
+ @written_object_count += 1
557
+ @written_object_count - 1
568
558
  end
569
559
 
570
560
  # Convert array to binary format and add it to the object table
571
561
  def array_to_binary(val)
572
562
  saved_object_count = @written_object_count
573
563
  @written_object_count += 1
564
+ #@object_refs += val.value.size
574
565
 
575
- bdata = Binary.type_bytes(0b1010, val.value.size) +
576
- Binary.pack_int_array_with_size(@object_ref_size, val.value.collect { |v| v.to_binary(self) })
566
+ values = val.value.map { |v| v.to_binary(self) }
567
+ bdata = Binary.type_bytes(0b1010, val.value.size) <<
568
+ Binary.pack_int_array_with_size(object_ref_size(@object_refs),
569
+ values)
577
570
 
578
571
  @object_table[saved_object_count] = bdata
579
- return saved_object_count
572
+ saved_object_count
580
573
  end
581
574
 
582
575
  # Convert dictionary to binary format and add it to the object table
@@ -584,11 +577,13 @@ module CFPropertyList
584
577
  saved_object_count = @written_object_count
585
578
  @written_object_count += 1
586
579
 
587
- keys_and_values = val.value.keys.collect { |k| CFString.new(k).to_binary(self) }
588
- keys_and_values += val.value.keys.collect { |k| val.value[k].to_binary(self) }
589
-
590
- bdata = Binary.type_bytes(0b1101,val.value.size) +
591
- Binary.pack_int_array_with_size(@object_ref_size, keys_and_values)
580
+ #@object_refs += val.value.keys.size * 2
581
+
582
+ keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
583
+ keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
584
+
585
+ bdata = Binary.type_bytes(0b1101,val.value.size) <<
586
+ Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
592
587
 
593
588
  @object_table[saved_object_count] = bdata
594
589
  return saved_object_count