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.
Files changed (2) hide show
  1. data/lib/rbBinaryCFPropertyList.rb +95 -198
  2. 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
- @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
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
- object_offset = 8
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
- 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
78
+ table_offset = next_offset
79
+ offset_size = Binary.bytes_needed(table_offset)
97
80
 
98
- offsets.each do |offset|
99
- binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
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 << [@count_objects].pack("x4N")
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
- format = ["C", "n", "N", "N"][nbytes-1]
381
-
382
- if(nbytes == 3) then
383
- val = [int].pack(format)
384
- return val.slice(-3)
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
- # 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
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
- # 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
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
- @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'))
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
- @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
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
- return val.bytesize
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 = false
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("6",Binary.charset_strlen(val,"UTF-16BE")) # 6 is 0110, unicode string (utf16be)
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
- bdata = Binary.type_bytes("5",val.bytesize) # 5 is 0101 which is an ASCII string (seems to be ASCII encoded)
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("1", nbytes) # 1 is 0001, type indicator for integer
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("2",3) # 2 is 0010, type indicator for reals
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("3", 3) # 3 is 0011, type indicator for date
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("4", val.bytesize) # a is 1000, type indicator for data
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("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
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
- bdata = Binary.type_bytes("d",val.value.size) # d=1101, type indicator for dictionary
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.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
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
metadata CHANGED
@@ -6,7 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 2
7
7
  - 0
8
8
  - 13
9
- version: 2.0.13
9
+ - 1
10
+ version: 2.0.13.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Christian Kruse