CFPropertyList 2.0.13a → 2.0.13
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 +198 -95
- metadata +6 -8
@@ -7,6 +7,9 @@ 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
|
10
13
|
@object_refs = 0
|
11
14
|
|
12
15
|
@written_object_count = 0
|
@@ -56,6 +59,9 @@ module CFPropertyList
|
|
56
59
|
def to_str(opts={})
|
57
60
|
@unique_table = {}
|
58
61
|
@count_objects = 0
|
62
|
+
@string_size = 0
|
63
|
+
@int_size = 0
|
64
|
+
@misc_size = 0
|
59
65
|
@object_refs = 0
|
60
66
|
|
61
67
|
@written_object_count = 0
|
@@ -65,32 +71,36 @@ module CFPropertyList
|
|
65
71
|
@offsets = []
|
66
72
|
|
67
73
|
binary_str = "bplist00"
|
74
|
+
unique_and_count_values(opts[:root])
|
68
75
|
|
69
|
-
@
|
70
|
-
@object_ref_size = Binary.bytes_needed(@
|
76
|
+
@count_objects += @unique_table.size
|
77
|
+
@object_ref_size = Binary.bytes_needed(@count_objects)
|
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
|
71
86
|
|
72
87
|
opts[:root].to_binary(self)
|
73
88
|
|
74
|
-
|
75
|
-
offsets =
|
76
|
-
binary_str << @object_table.join
|
89
|
+
object_offset = 8
|
90
|
+
offsets = []
|
77
91
|
|
78
|
-
|
79
|
-
|
92
|
+
0.upto(@object_table.size-1) do |i|
|
93
|
+
binary_str << @object_table[i]
|
94
|
+
offsets[i] = object_offset
|
95
|
+
object_offset += @object_table[i].bytesize
|
96
|
+
end
|
80
97
|
|
81
|
-
|
82
|
-
|
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
|
98
|
+
offsets.each do |offset|
|
99
|
+
binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
|
90
100
|
end
|
91
101
|
|
92
102
|
binary_str << [offset_size, @object_ref_size].pack("x6CC")
|
93
|
-
binary_str << [@
|
103
|
+
binary_str << [@count_objects].pack("x4N")
|
94
104
|
binary_str << [0].pack("x4N")
|
95
105
|
binary_str << [table_offset].pack("x4N")
|
96
106
|
|
@@ -342,35 +352,39 @@ module CFPropertyList
|
|
342
352
|
end
|
343
353
|
protected :read_binary_object_at
|
344
354
|
|
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
|
+
|
345
378
|
# pack an +int+ of +nbytes+ with size
|
346
379
|
def Binary.pack_it_with_size(nbytes,int)
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
return
|
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")
|
380
|
+
format = ["C", "n", "N", "N"][nbytes-1]
|
381
|
+
|
382
|
+
if(nbytes == 3) then
|
383
|
+
val = [int].pack(format)
|
384
|
+
return val.slice(-3)
|
373
385
|
end
|
386
|
+
|
387
|
+
return [int].pack(format)
|
374
388
|
end
|
375
389
|
|
376
390
|
# calculate how many bytes are needed to save +count+
|
@@ -390,53 +404,130 @@ module CFPropertyList
|
|
390
404
|
return nbytes
|
391
405
|
end
|
392
406
|
|
393
|
-
#
|
394
|
-
def Binary.
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
return bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
|
407
|
+
# create integer bytes of +int+
|
408
|
+
def Binary.int_bytes(int)
|
409
|
+
intbytes = ""
|
410
|
+
|
411
|
+
if(int >= 0) then
|
412
|
+
if (int <= 0xFF) then
|
413
|
+
intbytes = "\x10"+[int].pack("c") # 1 byte integer
|
414
|
+
elsif(int <= 0xFFFF) then
|
415
|
+
intbytes = "\x11"+[int].pack("n") # 2 byte integer
|
416
|
+
elsif(int <= 0xFFFFFFFF) then
|
417
|
+
intbytes = "\x12"+[int].pack("N") # 4 byte integer
|
418
|
+
elsif(int <= 0x7FFFFFFFFFFFFFFF)
|
419
|
+
intbytes = "\x13"+[int >> 32, int & 0xFFFFFFFF].pack("NN") # 8 byte integer
|
407
420
|
else
|
408
421
|
raise CFFormatError.new("Integer too large: #{int}")
|
409
422
|
end
|
423
|
+
else
|
424
|
+
intbytes = "\x13"+[int >> 32, int & 0xFFFFFFFF].pack("NN") # 8 byte integer
|
410
425
|
end
|
426
|
+
|
427
|
+
return intbytes;
|
411
428
|
end
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
429
|
+
|
430
|
+
# Create a type byte for binary format as defined by apple
|
431
|
+
def Binary.type_bytes(type,type_len)
|
432
|
+
optional_int = ""
|
433
|
+
|
434
|
+
if(type_len < 15) then
|
435
|
+
type += sprintf("%x",type_len)
|
436
|
+
else
|
437
|
+
type += "f"
|
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
|
421
454
|
end
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
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
|
473
|
+
end
|
474
|
+
|
475
|
+
@count_objects += 1
|
476
|
+
@int_size += Binary.bytes_size_int(cnt)
|
477
|
+
@misc_size += 1 # marker byte for array
|
478
|
+
return
|
479
|
+
elsif(value.is_a?(CFDictionary)) then
|
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'))
|
428
489
|
end
|
490
|
+
|
491
|
+
@object_refs += 2 # both, key and value, are refs
|
492
|
+
@unique_table[k] += 1
|
493
|
+
unique_and_count_values(v)
|
429
494
|
end
|
430
|
-
|
431
|
-
|
432
|
-
|
495
|
+
|
496
|
+
@count_objects += 1
|
497
|
+
@misc_size += 1 # marker byte for dict
|
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
|
433
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'))
|
513
|
+
end
|
514
|
+
|
515
|
+
@unique_table[val] += 1
|
434
516
|
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
|
435
527
|
|
436
|
-
|
437
|
-
return str.scan(/[\x80-\xFF]/m).size == 0
|
528
|
+
return val.bytesize
|
438
529
|
end
|
439
|
-
|
530
|
+
|
440
531
|
# Uniques and transforms a string value to binary format and adds it to the object table
|
441
532
|
def string_to_binary(val)
|
442
533
|
saved_object_count = -1
|
@@ -446,19 +537,23 @@ module CFPropertyList
|
|
446
537
|
@written_object_count += 1
|
447
538
|
|
448
539
|
@unique_table[val] = saved_object_count
|
449
|
-
utf16 =
|
540
|
+
utf16 = false
|
541
|
+
|
542
|
+
val.each_byte do |b|
|
543
|
+
if(b > 127) then
|
544
|
+
utf16 = true
|
545
|
+
break
|
546
|
+
end
|
547
|
+
end
|
450
548
|
|
451
|
-
utf8_strlen = 0
|
452
549
|
if(utf16) then
|
453
|
-
utf8_strlen = Binary.charset_strlen(val, "UTF-8")
|
454
550
|
val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
|
455
|
-
bdata = Binary.type_bytes(
|
551
|
+
bdata = Binary.type_bytes("6",Binary.charset_strlen(val,"UTF-16BE")) # 6 is 0110, unicode string (utf16be)
|
456
552
|
|
457
553
|
val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
|
458
554
|
@object_table[saved_object_count] = bdata + val
|
459
555
|
else
|
460
|
-
|
461
|
-
bdata = Binary.type_bytes(0b0101,val.bytesize)
|
556
|
+
bdata = Binary.type_bytes("5",val.bytesize) # 5 is 0101 which is an ASCII string (seems to be ASCII encoded)
|
462
557
|
@object_table[saved_object_count] = bdata + val
|
463
558
|
end
|
464
559
|
else
|
@@ -476,7 +571,7 @@ module CFPropertyList
|
|
476
571
|
nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
|
477
572
|
nbytes = 3 if value < 0 # 8 byte integer, since signed
|
478
573
|
|
479
|
-
bdata = Binary.type_bytes(
|
574
|
+
bdata = Binary.type_bytes("1", nbytes) # 1 is 0001, type indicator for integer
|
480
575
|
buff = ""
|
481
576
|
|
482
577
|
if(nbytes < 3) then
|
@@ -501,7 +596,7 @@ module CFPropertyList
|
|
501
596
|
|
502
597
|
# Codes a real value to binary format
|
503
598
|
def real_to_binary(val)
|
504
|
-
bdata = Binary.type_bytes(
|
599
|
+
bdata = Binary.type_bytes("2",3) # 2 is 0010, type indicator for reals
|
505
600
|
buff = [val].pack("d")
|
506
601
|
return bdata + buff.reverse
|
507
602
|
end
|
@@ -529,7 +624,7 @@ module CFPropertyList
|
|
529
624
|
|
530
625
|
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
|
531
626
|
|
532
|
-
bdata = Binary.type_bytes(
|
627
|
+
bdata = Binary.type_bytes("3", 3) # 3 is 0011, type indicator for date
|
533
628
|
@object_table[saved_object_count] = bdata + [val].pack("d").reverse
|
534
629
|
|
535
630
|
return saved_object_count
|
@@ -549,7 +644,7 @@ module CFPropertyList
|
|
549
644
|
saved_object_count = @written_object_count
|
550
645
|
@written_object_count += 1
|
551
646
|
|
552
|
-
bdata = Binary.type_bytes(
|
647
|
+
bdata = Binary.type_bytes("4", val.bytesize) # a is 1000, type indicator for data
|
553
648
|
@object_table[saved_object_count] = bdata + val
|
554
649
|
|
555
650
|
return saved_object_count
|
@@ -560,8 +655,11 @@ module CFPropertyList
|
|
560
655
|
saved_object_count = @written_object_count
|
561
656
|
@written_object_count += 1
|
562
657
|
|
563
|
-
bdata = Binary.type_bytes(
|
564
|
-
|
658
|
+
bdata = Binary.type_bytes("a", val.value.size) # a is 1010, type indicator for arrays
|
659
|
+
|
660
|
+
val.value.each do |v|
|
661
|
+
bdata << Binary.pack_it_with_size(@object_ref_size, v.to_binary(self));
|
662
|
+
end
|
565
663
|
|
566
664
|
@object_table[saved_object_count] = bdata
|
567
665
|
return saved_object_count
|
@@ -572,12 +670,17 @@ module CFPropertyList
|
|
572
670
|
saved_object_count = @written_object_count
|
573
671
|
@written_object_count += 1
|
574
672
|
|
575
|
-
|
576
|
-
[ CFString.new(k).to_binary(self), v.to_binary(self) ]
|
577
|
-
end.flatten
|
673
|
+
bdata = Binary.type_bytes("d",val.value.size) # d=1101, type indicator for dictionary
|
578
674
|
|
579
|
-
|
580
|
-
|
675
|
+
val.value.each_key do |k|
|
676
|
+
str = CFString.new(k)
|
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
|
581
684
|
|
582
685
|
@object_table[saved_object_count] = bdata
|
583
686
|
return saved_object_count
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: CFPropertyList
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 2.0.
|
8
|
+
- 13
|
9
|
+
version: 2.0.13
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Christian Kruse
|
@@ -83,13 +83,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
|
-
- - "
|
86
|
+
- - ">="
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
segments:
|
89
|
-
-
|
90
|
-
|
91
|
-
- 1
|
92
|
-
version: 1.3.1
|
89
|
+
- 0
|
90
|
+
version: "0"
|
93
91
|
requirements: []
|
94
92
|
|
95
93
|
rubyforge_project: cfpropertylist
|