CFPropertyList 2.0.13 → 2.0.13.1
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.
- data/lib/rbBinaryCFPropertyList.rb +95 -198
- metadata +2 -1
@@ -7,9 +7,6 @@ module CFPropertyList
|
|
7
7
|
def load(opts)
|
8
8
|
@unique_table = {}
|
9
9
|
@count_objects = 0
|
10
|
-
@string_size = 0
|
11
|
-
@int_size = 0
|
12
|
-
@misc_size = 0
|
13
10
|
@object_refs = 0
|
14
11
|
|
15
12
|
@written_object_count = 0
|
@@ -59,9 +56,6 @@ module CFPropertyList
|
|
59
56
|
def to_str(opts={})
|
60
57
|
@unique_table = {}
|
61
58
|
@count_objects = 0
|
62
|
-
@string_size = 0
|
63
|
-
@int_size = 0
|
64
|
-
@misc_size = 0
|
65
59
|
@object_refs = 0
|
66
60
|
|
67
61
|
@written_object_count = 0
|
@@ -71,36 +65,32 @@ module CFPropertyList
|
|
71
65
|
@offsets = []
|
72
66
|
|
73
67
|
binary_str = "bplist00"
|
74
|
-
unique_and_count_values(opts[:root])
|
75
68
|
|
76
|
-
@
|
77
|
-
@object_ref_size = Binary.bytes_needed(@
|
78
|
-
|
79
|
-
file_size = @string_size + @int_size + @misc_size + @object_refs * @object_ref_size + 40
|
80
|
-
offset_size = Binary.bytes_needed(file_size)
|
81
|
-
table_offset = file_size - 32
|
82
|
-
|
83
|
-
@object_table = []
|
84
|
-
@written_object_count = 0
|
85
|
-
@unique_table = {} # we needed it to calculate several values, but now we need an empty table
|
69
|
+
@object_refs = count_object_refs(opts[:root])
|
70
|
+
@object_ref_size = Binary.bytes_needed(@object_refs)
|
86
71
|
|
87
72
|
opts[:root].to_binary(self)
|
88
73
|
|
89
|
-
|
90
|
-
offsets =
|
74
|
+
next_offset = 8
|
75
|
+
offsets = @object_table.collect { |object| offset = next_offset; next_offset += object.bytesize; offset }
|
76
|
+
binary_str << @object_table.join
|
91
77
|
|
92
|
-
|
93
|
-
|
94
|
-
offsets[i] = object_offset
|
95
|
-
object_offset += @object_table[i].bytesize
|
96
|
-
end
|
78
|
+
table_offset = next_offset
|
79
|
+
offset_size = Binary.bytes_needed(table_offset)
|
97
80
|
|
98
|
-
|
99
|
-
|
81
|
+
if offset_size < 8
|
82
|
+
# Fast path: encode the entire offset array at once.
|
83
|
+
binary_str << offsets.pack((%w(C n N N)[offset_size - 1]) + '*')
|
84
|
+
else
|
85
|
+
# Slow path: host may be little or big endian, must pack each offset
|
86
|
+
# separately.
|
87
|
+
offsets.each do |offset|
|
88
|
+
binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
|
89
|
+
end
|
100
90
|
end
|
101
91
|
|
102
92
|
binary_str << [offset_size, @object_ref_size].pack("x6CC")
|
103
|
-
binary_str << [@
|
93
|
+
binary_str << [@object_table.size].pack("x4N")
|
104
94
|
binary_str << [0].pack("x4N")
|
105
95
|
binary_str << [table_offset].pack("x4N")
|
106
96
|
|
@@ -352,39 +342,35 @@ module CFPropertyList
|
|
352
342
|
end
|
353
343
|
protected :read_binary_object_at
|
354
344
|
|
355
|
-
# calculate the bytes needed for a size integer value
|
356
|
-
def Binary.bytes_size_int(int)
|
357
|
-
nbytes = 0
|
358
|
-
|
359
|
-
nbytes += 2 if int > 0xE # 2 bytes int
|
360
|
-
nbytes += 1 if int > 0xFF # 3 bytes int
|
361
|
-
nbytes += 2 if int > 0xFFFF # 5 bytes int
|
362
|
-
|
363
|
-
return nbytes
|
364
|
-
end
|
365
|
-
|
366
|
-
# Calculate the byte needed for a „normal” integer value
|
367
|
-
def Binary.bytes_int(int)
|
368
|
-
nbytes = 1
|
369
|
-
|
370
|
-
nbytes += 1 if int > 0xFF # 2 byte int
|
371
|
-
nbytes += 2 if int > 0xFFFF # 4 byte int
|
372
|
-
nbytes += 4 if int > 0xFFFFFFFF # 8 byte int
|
373
|
-
nbytes += 7 if int < 0 # 8 byte int (since it is signed)
|
374
|
-
|
375
|
-
return nbytes + 1 # one „marker” byte
|
376
|
-
end
|
377
|
-
|
378
345
|
# pack an +int+ of +nbytes+ with size
|
379
346
|
def Binary.pack_it_with_size(nbytes,int)
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
return
|
347
|
+
case nbytes
|
348
|
+
when 1
|
349
|
+
return [int].pack('c')
|
350
|
+
when 2
|
351
|
+
return [int].pack('n')
|
352
|
+
when 4
|
353
|
+
return [int].pack('N')
|
354
|
+
when 8
|
355
|
+
return [int >> 32, int & 0xFFFFFFFF].pack('NN')
|
356
|
+
else
|
357
|
+
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def Binary.pack_int_array_with_size(nbytes, array)
|
362
|
+
case nbytes
|
363
|
+
when 1
|
364
|
+
array.pack('C*')
|
365
|
+
when 2
|
366
|
+
array.pack('n*')
|
367
|
+
when 4
|
368
|
+
array.pack('N*')
|
369
|
+
when 8
|
370
|
+
array.collect { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
|
371
|
+
else
|
372
|
+
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
|
385
373
|
end
|
386
|
-
|
387
|
-
return [int].pack(format)
|
388
374
|
end
|
389
375
|
|
390
376
|
# calculate how many bytes are needed to save +count+
|
@@ -404,130 +390,53 @@ module CFPropertyList
|
|
404
390
|
return nbytes
|
405
391
|
end
|
406
392
|
|
407
|
-
#
|
408
|
-
def Binary.
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
393
|
+
# Create a type byte for binary format as defined by apple
|
394
|
+
def Binary.type_bytes(type, length)
|
395
|
+
if length < 15
|
396
|
+
return [(type << 4) | length].pack('C')
|
397
|
+
else
|
398
|
+
bytes = [(type << 4) | 0xF]
|
399
|
+
if length <= 0xFF
|
400
|
+
return bytes.push(0x10, length).pack('CCC') # 1 byte length
|
401
|
+
elsif length <= 0xFFFF
|
402
|
+
return bytes.push(0x11, length).pack('CCn') # 2 byte length
|
403
|
+
elsif length <= 0xFFFFFFFF
|
404
|
+
return bytes.push(0x12, length).pack('CCN') # 4 byte length
|
405
|
+
elsif length <= 0x7FFFFFFFFFFFFFFF
|
406
|
+
return bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
|
420
407
|
else
|
421
408
|
raise CFFormatError.new("Integer too large: #{int}")
|
422
409
|
end
|
423
|
-
else
|
424
|
-
intbytes = "\x13"+[int >> 32, int & 0xFFFFFFFF].pack("NN") # 8 byte integer
|
425
410
|
end
|
426
|
-
|
427
|
-
return intbytes;
|
428
411
|
end
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
optional_int = Binary.int_bytes(type_len)
|
439
|
-
end
|
440
|
-
|
441
|
-
return [type].pack("H*") + optional_int
|
442
|
-
end
|
443
|
-
|
444
|
-
# „unique” and count values. „Unique” means, several objects (e.g. strings)
|
445
|
-
# will only be saved once and referenced later
|
446
|
-
def unique_and_count_values(value)
|
447
|
-
# no uniquing for other types than CFString and CFData
|
448
|
-
if(value.is_a?(CFInteger) || value.is_a?(CFReal)) then
|
449
|
-
val = value.value
|
450
|
-
if(value.is_a?(CFInteger)) then
|
451
|
-
@int_size += Binary.bytes_int(val)
|
452
|
-
else
|
453
|
-
@misc_size += 9 # 9 bytes (8 + marker byte) for real
|
454
|
-
end
|
455
|
-
|
456
|
-
@count_objects += 1
|
457
|
-
return
|
458
|
-
elsif(value.is_a?(CFDate)) then
|
459
|
-
@misc_size += 9
|
460
|
-
@count_objects += 1
|
461
|
-
return
|
462
|
-
elsif(value.is_a?(CFBoolean)) then
|
463
|
-
@count_objects += 1
|
464
|
-
@misc_size += 1
|
465
|
-
return
|
466
|
-
elsif(value.is_a?(CFArray)) then
|
467
|
-
cnt = 0
|
468
|
-
|
469
|
-
value.value.each do |v|
|
470
|
-
cnt += 1
|
471
|
-
unique_and_count_values(v)
|
472
|
-
@object_refs += 1 # each array member is a ref
|
412
|
+
|
413
|
+
def count_object_refs(object)
|
414
|
+
case object
|
415
|
+
when CFArray
|
416
|
+
contained_refs = 0
|
417
|
+
object.value.each do |element|
|
418
|
+
if CFArray === element || CFDictionary === element
|
419
|
+
contained_refs += count_object_refs(element)
|
420
|
+
end
|
473
421
|
end
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
cnt = 0
|
481
|
-
|
482
|
-
value.value.each_pair do |k,v|
|
483
|
-
cnt += 1
|
484
|
-
|
485
|
-
if(!@unique_table.has_key?(k))
|
486
|
-
@unique_table[k] = 0
|
487
|
-
@string_size += Binary.binary_strlen(k) + 1
|
488
|
-
@int_size += Binary.bytes_size_int(Binary.charset_strlen(k,'UTF-8'))
|
422
|
+
return object.value.size + contained_refs
|
423
|
+
when CFDictionary
|
424
|
+
contained_refs = 0
|
425
|
+
object.value.each_value do |value|
|
426
|
+
if CFArray === value || CFDictionary === value
|
427
|
+
contained_refs += count_object_refs(value)
|
489
428
|
end
|
490
|
-
|
491
|
-
@object_refs += 2 # both, key and value, are refs
|
492
|
-
@unique_table[k] += 1
|
493
|
-
unique_and_count_values(v)
|
494
429
|
end
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
@int_size += Binary.bytes_size_int(cnt)
|
499
|
-
return
|
500
|
-
elsif(value.is_a?(CFData)) then
|
501
|
-
val = value.decoded_value
|
502
|
-
@int_size += Binary.bytes_size_int(val.length)
|
503
|
-
@misc_size += val.length + 1
|
504
|
-
@count_objects += 1
|
505
|
-
return
|
506
|
-
end
|
507
|
-
|
508
|
-
val = value.value
|
509
|
-
if(!@unique_table.has_key?(val)) then
|
510
|
-
@unique_table[val] = 0
|
511
|
-
@string_size += Binary.binary_strlen(val) + 1
|
512
|
-
@int_size += Binary.bytes_size_int(Binary.charset_strlen(val,'UTF-8'))
|
430
|
+
return object.value.keys.size * 2 + contained_refs
|
431
|
+
else
|
432
|
+
return 0
|
513
433
|
end
|
514
|
-
|
515
|
-
@unique_table[val] += 1
|
516
434
|
end
|
517
|
-
protected :unique_and_count_values
|
518
|
-
|
519
|
-
# Counts the number of bytes the string will have when coded; utf-16be if non-ascii characters are present.
|
520
|
-
def Binary.binary_strlen(val)
|
521
|
-
val.each_byte do |b|
|
522
|
-
if(b > 127) then
|
523
|
-
val = Binary.charset_convert(val, 'UTF-8', 'UTF-16BE')
|
524
|
-
return val.bytesize
|
525
|
-
end
|
526
|
-
end
|
527
435
|
|
528
|
-
|
436
|
+
def Binary.ascii_string?(str)
|
437
|
+
return str.scan(/[\x80-\xFF]/m).size == 0
|
529
438
|
end
|
530
|
-
|
439
|
+
|
531
440
|
# Uniques and transforms a string value to binary format and adds it to the object table
|
532
441
|
def string_to_binary(val)
|
533
442
|
saved_object_count = -1
|
@@ -537,23 +446,19 @@ module CFPropertyList
|
|
537
446
|
@written_object_count += 1
|
538
447
|
|
539
448
|
@unique_table[val] = saved_object_count
|
540
|
-
utf16 =
|
541
|
-
|
542
|
-
val.each_byte do |b|
|
543
|
-
if(b > 127) then
|
544
|
-
utf16 = true
|
545
|
-
break
|
546
|
-
end
|
547
|
-
end
|
449
|
+
utf16 = !Binary.ascii_string?(val)
|
548
450
|
|
451
|
+
utf8_strlen = 0
|
549
452
|
if(utf16) then
|
453
|
+
utf8_strlen = Binary.charset_strlen(val, "UTF-8")
|
550
454
|
val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
|
551
|
-
bdata = Binary.type_bytes(
|
455
|
+
bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
|
552
456
|
|
553
457
|
val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
|
554
458
|
@object_table[saved_object_count] = bdata + val
|
555
459
|
else
|
556
|
-
|
460
|
+
utf8_strlen = val.bytesize
|
461
|
+
bdata = Binary.type_bytes(0b0101,val.bytesize)
|
557
462
|
@object_table[saved_object_count] = bdata + val
|
558
463
|
end
|
559
464
|
else
|
@@ -571,7 +476,7 @@ module CFPropertyList
|
|
571
476
|
nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
|
572
477
|
nbytes = 3 if value < 0 # 8 byte integer, since signed
|
573
478
|
|
574
|
-
bdata = Binary.type_bytes(
|
479
|
+
bdata = Binary.type_bytes(0b0001, nbytes)
|
575
480
|
buff = ""
|
576
481
|
|
577
482
|
if(nbytes < 3) then
|
@@ -596,7 +501,7 @@ module CFPropertyList
|
|
596
501
|
|
597
502
|
# Codes a real value to binary format
|
598
503
|
def real_to_binary(val)
|
599
|
-
bdata = Binary.type_bytes(
|
504
|
+
bdata = Binary.type_bytes(0b0010,3)
|
600
505
|
buff = [val].pack("d")
|
601
506
|
return bdata + buff.reverse
|
602
507
|
end
|
@@ -624,7 +529,7 @@ module CFPropertyList
|
|
624
529
|
|
625
530
|
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
|
626
531
|
|
627
|
-
bdata = Binary.type_bytes(
|
532
|
+
bdata = Binary.type_bytes(0b0011, 3)
|
628
533
|
@object_table[saved_object_count] = bdata + [val].pack("d").reverse
|
629
534
|
|
630
535
|
return saved_object_count
|
@@ -644,7 +549,7 @@ module CFPropertyList
|
|
644
549
|
saved_object_count = @written_object_count
|
645
550
|
@written_object_count += 1
|
646
551
|
|
647
|
-
bdata = Binary.type_bytes(
|
552
|
+
bdata = Binary.type_bytes(0b0100, val.bytesize)
|
648
553
|
@object_table[saved_object_count] = bdata + val
|
649
554
|
|
650
555
|
return saved_object_count
|
@@ -655,11 +560,8 @@ module CFPropertyList
|
|
655
560
|
saved_object_count = @written_object_count
|
656
561
|
@written_object_count += 1
|
657
562
|
|
658
|
-
bdata = Binary.type_bytes(
|
659
|
-
|
660
|
-
val.value.each do |v|
|
661
|
-
bdata << Binary.pack_it_with_size(@object_ref_size, v.to_binary(self));
|
662
|
-
end
|
563
|
+
bdata = Binary.type_bytes(0b1010, val.value.size) +
|
564
|
+
Binary.pack_int_array_with_size(@object_ref_size, val.value.collect { |v| v.to_binary(self) })
|
663
565
|
|
664
566
|
@object_table[saved_object_count] = bdata
|
665
567
|
return saved_object_count
|
@@ -670,17 +572,12 @@ module CFPropertyList
|
|
670
572
|
saved_object_count = @written_object_count
|
671
573
|
@written_object_count += 1
|
672
574
|
|
673
|
-
|
575
|
+
keys_and_values = val.value.collect do |k, v|
|
576
|
+
[ CFString.new(k).to_binary(self), v.to_binary(self) ]
|
577
|
+
end.flatten
|
674
578
|
|
675
|
-
val.value.
|
676
|
-
|
677
|
-
key = str.to_binary(self)
|
678
|
-
bdata << Binary.pack_it_with_size(@object_ref_size,key)
|
679
|
-
end
|
680
|
-
|
681
|
-
val.value.each_value do |v|
|
682
|
-
bdata << Binary.pack_it_with_size(@object_ref_size,v.to_binary(self))
|
683
|
-
end
|
579
|
+
bdata = Binary.type_bytes(0b1101,val.value.size) +
|
580
|
+
Binary.pack_int_array_with_size(@object_ref_size, keys_and_values)
|
684
581
|
|
685
582
|
@object_table[saved_object_count] = bdata
|
686
583
|
return saved_object_count
|