CFPropertyList 2.0.14 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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