CFPropertyList 2.0.7

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/README ADDED
@@ -0,0 +1,34 @@
1
+ CFPropertyList implementation
2
+ class to read, manipulate and write both XML and binary property list
3
+ files (plist(5)) as defined by Apple
4
+
5
+ == Example
6
+
7
+ # create a arbitrary data structure of basic data types
8
+ data = {
9
+ 'name' => 'John Doe',
10
+ 'missing' => true,
11
+ 'last_seen' => Time.now,
12
+ 'friends' => ['Jane Doe','Julian Doe'],
13
+ 'likes' => {
14
+ 'me' => false
15
+ }
16
+ }
17
+
18
+ # create CFPropertyList::List object
19
+ plist = CFPropertyList::List.new
20
+
21
+ # call CFPropertyList.guess() to create corresponding CFType values
22
+ plist.value = CFPropertyList.guess(data)
23
+
24
+ # write plist to file
25
+ plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
26
+
27
+ # … later, read it again
28
+ plist = CFPropertyList::List.new("example.plist")
29
+ data = CFPropertyList.native_types(plist.value)
30
+
31
+ Author:: Christian Kruse (mailto:cjk@wwwtech.de)
32
+ Copyright:: Copyright (c) 2010
33
+ License:: Distributes under the same terms as Ruby
34
+
@@ -0,0 +1,669 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # CFPropertyList implementation
4
+ # parser class to read, manipulate and write binary property list files (plist(5)) as defined by Apple
5
+ #
6
+ # Author:: Christian Kruse (mailto:cjk@wwwtech.de)
7
+ # Copyright:: Copyright (c) 2010
8
+ # License:: Distributes under the same terms as Ruby
9
+
10
+ module CFPropertyList
11
+ class Binary
12
+ # Read a binary plist file
13
+ def load(opts)
14
+ @unique_table = {}
15
+ @count_objects = 0
16
+ @string_size = 0
17
+ @int_size = 0
18
+ @misc_size = 0
19
+ @object_refs = 0
20
+
21
+ @written_object_count = 0
22
+ @object_table = []
23
+ @object_ref_size = 0
24
+
25
+ @offsets = []
26
+
27
+ fd = nil
28
+ if(opts.has_key?(:file)) then
29
+ fd = File.open(opts[:file],"rb")
30
+ file = opts[:file]
31
+ else
32
+ fd = StringIO.new(opts[:data],"rb")
33
+ file = "<string>"
34
+ end
35
+
36
+ # first, we read the trailer: 32 byte from the end
37
+ fd.seek(-32,IO::SEEK_END)
38
+ buff = fd.read(32)
39
+
40
+ offset_size, object_ref_size, number_of_objects, top_object, table_offset = buff.unpack "x6CCx4Nx4Nx4N"
41
+
42
+ # after that, get the offset table
43
+ fd.seek(table_offset, IO::SEEK_SET)
44
+ coded_offset_table = fd.read(number_of_objects * offset_size)
45
+ raise CFFormatError.new("#{file}: Format error!") unless coded_offset_table.bytesize == number_of_objects * offset_size
46
+
47
+ @count_objects = number_of_objects
48
+
49
+ # decode offset table
50
+ formats = ["","C*","n*","(H6)*","N*"]
51
+ @offsets = coded_offset_table.unpack(formats[offset_size])
52
+ if(offset_size == 3) then
53
+ 0.upto(@offsets.count-1) { |i| @offsets[i] = @offsets[i].to_i(16) }
54
+ end
55
+
56
+ @object_ref_size = object_ref_size
57
+ val = read_binary_object_at(file,fd,top_object)
58
+
59
+ fd.close
60
+ return val
61
+ end
62
+
63
+
64
+ # Convert CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray
65
+ def to_str(opts={})
66
+ @unique_table = {}
67
+ @count_objects = 0
68
+ @string_size = 0
69
+ @int_size = 0
70
+ @misc_size = 0
71
+ @object_refs = 0
72
+
73
+ @written_object_count = 0
74
+ @object_table = []
75
+ @object_ref_size = 0
76
+
77
+ @offsets = []
78
+
79
+ binary_str = "bplist00"
80
+ unique_and_count_values(opts[:root])
81
+
82
+ @count_objects += @unique_table.count
83
+ @object_ref_size = Binary.bytes_needed(@count_objects)
84
+
85
+ file_size = @string_size + @int_size + @misc_size + @object_refs * @object_ref_size + 40
86
+ offset_size = Binary.bytes_needed(file_size)
87
+ table_offset = file_size - 32
88
+
89
+ @object_table = []
90
+ @written_object_count = 0
91
+ @unique_table = {} # we needed it to calculate several values, but now we need an empty table
92
+
93
+ opts[:root].to_binary(self)
94
+
95
+ object_offset = 8
96
+ offsets = []
97
+
98
+ 0.upto(@object_table.count-1) do |i|
99
+ binary_str += @object_table[i]
100
+ offsets[i] = object_offset
101
+ object_offset += @object_table[i].bytesize
102
+ end
103
+
104
+ offsets.each do |offset|
105
+ binary_str += Binary.pack_it_with_size(offset_size,offset)
106
+ end
107
+
108
+ binary_str += [offset_size, @object_ref_size].pack("x6CC")
109
+ binary_str += [@count_objects].pack("x4N")
110
+ binary_str += [0].pack("x4N")
111
+ binary_str += [table_offset].pack("x4N")
112
+
113
+ return binary_str
114
+ end
115
+
116
+ # read a „null” type (i.e. null byte, marker byte, bool value)
117
+ def read_binary_null_type(length)
118
+ case length
119
+ when 0 then return 0 # null byte
120
+ when 8 then return CFBoolean.new(false)
121
+ when 9 then return CFBoolean.new(true)
122
+ when 15 then return 15 # fill type
123
+ end
124
+
125
+ raise CFFormatError.new("unknown null type: #{length}")
126
+ end
127
+ protected :read_binary_null_type
128
+
129
+ # read a binary int value
130
+ def read_binary_int(fname,fd,length)
131
+ raise CFFormatError.new("Integer greater than 8 bytes: #{length}") if length > 3
132
+
133
+ nbytes = 1 << length
134
+
135
+ val = nil
136
+ buff = fd.read(nbytes)
137
+
138
+ case length
139
+ when 0 then
140
+ val = buff.unpack("C")
141
+ val = val[0]
142
+ when 1 then
143
+ val = buff.unpack("n")
144
+ val = val[0]
145
+ when 2 then
146
+ val = buff.unpack("N")
147
+ val = val[0]
148
+ when 3
149
+ hiword,loword = buff.unpack("NN")
150
+ val = hiword << 32 | loword
151
+ end
152
+
153
+ return CFInteger.new(val);
154
+ end
155
+ protected :read_binary_int
156
+
157
+ # read a binary real value
158
+ def read_binary_real(fname,fd,length)
159
+ raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
160
+
161
+ nbytes = 1 << length
162
+ val = nil
163
+ buff = fd.read(nbytes)
164
+
165
+ case length
166
+ when 0 then # 1 byte float? must be an error
167
+ raise CFFormatError.new("got #{length+1} byte float, must be an error!")
168
+ when 1 then # 2 byte float? must be an error
169
+ raise CFFormatError.new("got #{length+1} byte float, must be an error!")
170
+ when 2 then
171
+ val = buff.reverse.unpack("f")
172
+ val = val[0]
173
+ when 3 then
174
+ val = buff.reverse.unpack("d")
175
+ val = val[0]
176
+ end
177
+
178
+ return CFReal.new(val)
179
+ end
180
+ protected :read_binary_real
181
+
182
+ # read a binary date value
183
+ def read_binary_date(fname,fd,length)
184
+ raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
185
+
186
+ nbytes = 1 << length
187
+ val = nil
188
+ buff = fd.read(nbytes)
189
+
190
+ case length
191
+ when 0 then # 1 byte CFDate is an error
192
+ raise CFFormatError.new("#{length+1} byte CFDate, error")
193
+ when 1 then # 2 byte CFDate is an error
194
+ raise CFFormatError.new("#{length+1} byte CFDate, error")
195
+ when 2 then
196
+ val = buff.reverse.unpack("f")
197
+ val = val[0]
198
+ when 3 then
199
+ val = buff.reverse.unpack("d")
200
+ val = val[0]
201
+ end
202
+
203
+ return CFDate.new(val,CFDate::TIMESTAMP_APPLE)
204
+ end
205
+ protected :read_binary_date
206
+
207
+ # Read a binary data value
208
+ def read_binary_data(fname,fd,length)
209
+ buff = "";
210
+ buff = fd.read(length) if length > 0
211
+ return CFData.new(buff,CFData::DATA_RAW)
212
+ end
213
+ protected :read_binary_data
214
+
215
+ # Read a binary string value
216
+ def read_binary_string(fname,fd,length)
217
+ buff = ""
218
+ buff = fd.read(length) if length > 0
219
+
220
+ @unique_table[buff] = true unless @unique_table.has_key?(buff)
221
+ return CFString.new(buff)
222
+ end
223
+ protected :read_binary_string
224
+
225
+ # Convert the given string from one charset to another
226
+ def Binary.charset_convert(str,from,to="UTF-8")
227
+ return str.clone.force_encoding(from).encode(to) if str.respond_to?("encode")
228
+ return Iconv.conv(to,from,str)
229
+ end
230
+
231
+ # Count characters considering character set
232
+ def Binary.charset_strlen(str,charset="UTF-8")
233
+ return str.length if str.respond_to?("encode")
234
+
235
+ str = Iconv.conv("UTF-8",charset,str) if charset != "UTF-8"
236
+ return str.scan(/./mu).size
237
+ end
238
+
239
+ # Read a unicode string value, coded as UTF-16BE
240
+ def read_binary_unicode_string(fname,fd,length)
241
+ # The problem is: we get the length of the string IN CHARACTERS;
242
+ # since a char in UTF-16 can be 16 or 32 bit long, we don't really know
243
+ # how long the string is in bytes
244
+ buff = fd.read(2*length)
245
+
246
+ @unique_table[buff] = true unless @unique_table.has_key?(buff)
247
+ return CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
248
+ end
249
+ protected :read_binary_unicode_string
250
+
251
+ # Read an binary array value, including contained objects
252
+ def read_binary_array(fname,fd,length)
253
+ ary = []
254
+
255
+ # first: read object refs
256
+ if(length != 0) then
257
+ buff = fd.read(length * @object_ref_size)
258
+ objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
259
+
260
+ # now: read objects
261
+ 0.upto(length-1) do |i|
262
+ object = read_binary_object_at(fname,fd,objects[i])
263
+ ary.push object
264
+ end
265
+ end
266
+
267
+ return CFArray.new(ary)
268
+ end
269
+ protected :read_binary_array
270
+
271
+ # Read a dictionary value, including contained objects
272
+ def read_binary_dict(fname,fd,length)
273
+ dict = {}
274
+
275
+ # first: read keys
276
+ if(length != 0) then
277
+ buff = fd.read(length * @object_ref_size)
278
+ keys = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
279
+
280
+ # second: read object refs
281
+ buff = fd.read(length * @object_ref_size)
282
+ objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
283
+
284
+ # read real keys and objects
285
+ 0.upto(length-1) do |i|
286
+ key = read_binary_object_at(fname,fd,keys[i])
287
+ object = read_binary_object_at(fname,fd,objects[i])
288
+ dict[key.value] = object
289
+ end
290
+ end
291
+
292
+ return CFDictionary.new(dict)
293
+ end
294
+ protected :read_binary_dict
295
+
296
+ # Read an object type byte, decode it and delegate to the correct reader function
297
+ def read_binary_object(fname,fd)
298
+ # first: read the marker byte
299
+ buff = fd.read(1)
300
+
301
+ object_length = buff.unpack("C*")
302
+ object_length = object_length[0] & 0xF
303
+
304
+ buff = buff.unpack("H*")
305
+ object_type = buff[0][0].chr
306
+
307
+ if(object_type != "0" && object_length == 15) then
308
+ object_length = read_binary_object(fname,fd)
309
+ object_length = object_length.value
310
+ end
311
+
312
+ retval = nil
313
+ case object_type
314
+ when '0' then # null, false, true, fillbyte
315
+ retval = read_binary_null_type(object_length)
316
+ when '1' then # integer
317
+ retval = read_binary_int(fname,fd,object_length)
318
+ when '2' then # real
319
+ retval = read_binary_real(fname,fd,object_length)
320
+ when '3' then # date
321
+ retval = read_binary_date(fname,fd,object_length)
322
+ when '4' then # data
323
+ retval = read_binary_data(fname,fd,object_length)
324
+ when '5' then # byte string, usually utf8 encoded
325
+ retval = read_binary_string(fname,fd,object_length)
326
+ when '6' then # unicode string (utf16be)
327
+ retval = read_binary_unicode_string(fname,fd,object_length)
328
+ when 'a' then # array
329
+ retval = read_binary_array(fname,fd,object_length)
330
+ when 'd' then # dictionary
331
+ retval = read_binary_dict(fname,fd,object_length)
332
+ end
333
+
334
+ return retval
335
+ end
336
+ protected :read_binary_object
337
+
338
+ # Read an object type byte at position $pos, decode it and delegate to the correct reader function
339
+ def read_binary_object_at(fname,fd,pos)
340
+ position = @offsets[pos]
341
+ fd.seek(position,IO::SEEK_SET)
342
+ return read_binary_object(fname,fd)
343
+ end
344
+ protected :read_binary_object_at
345
+
346
+ # calculate the bytes needed for a size integer value
347
+ def Binary.bytes_size_int(int)
348
+ nbytes = 0
349
+
350
+ nbytes += 2 if int > 0xE # 2 bytes int
351
+ nbytes += 2 if int > 0xFF # 3 bytes int
352
+ nbytes += 2 if int > 0xFFFF # 5 bytes int
353
+
354
+ return nbytes
355
+ end
356
+
357
+ # Calculate the byte needed for a „normal” integer value
358
+ def Binary.bytes_int(int)
359
+ nbytes = 1
360
+
361
+ nbytes += 1 if int > 0xFF # 2 byte int
362
+ nbytes += 2 if int > 0xFFFF # 4 byte int
363
+ nbytes += 4 if int > 0xFFFFFFFF # 8 byte int
364
+ nbytes += 7 if int < 0 # 8 byte int (since it is signed)
365
+
366
+ return nbytes + 1 # one „marker” byte
367
+ end
368
+
369
+ # pack an +int+ of +nbytes+ with size
370
+ def Binary.pack_it_with_size(nbytes,int)
371
+ format = ["C", "n", "N", "N"][nbytes-1]
372
+
373
+ if(nbytes == 3) then
374
+ val = [int].pack(format)
375
+ return val.slice(-3)
376
+ end
377
+
378
+ return [int].pack(format)
379
+ end
380
+
381
+ # calculate how many bytes are needed to save +count+
382
+ def Binary.bytes_needed(count)
383
+ nbytes = 0
384
+
385
+ while count >= 1 do
386
+ nbytes += 1
387
+ count /= 256
388
+ end
389
+
390
+ return nbytes
391
+ end
392
+
393
+ # create integer bytes of +int+
394
+ def Binary.int_bytes(int)
395
+ intbytes = ""
396
+
397
+ if(int > 0xFFFF) then
398
+ intbytes = "\x12"+[int].pack("N") # 4 byte integer
399
+ elsif(int > 0xFF) then
400
+ intbytes = "\x11"+[int].pack("n") # 2 byte integer
401
+ else
402
+ intbytes = "\x10"+[int].pack("C") # 8 byte integer
403
+ end
404
+
405
+ return intbytes;
406
+ end
407
+
408
+ # Create a type byte for binary format as defined by apple
409
+ def Binary.type_bytes(type,type_len)
410
+ optional_int = ""
411
+
412
+ if(type_len < 15) then
413
+ type += sprintf("%x",type_len)
414
+ else
415
+ type += "f"
416
+ optional_int = Binary.int_bytes(type_len)
417
+ end
418
+
419
+ return [type].pack("H*") + optional_int
420
+ end
421
+
422
+ # „unique” and count values. „Unique” means, several objects (e.g. strings)
423
+ # will only be saved once and referenced later
424
+ def unique_and_count_values(value)
425
+ # no uniquing for other types than CFString and CFData
426
+ if(value.is_a?(CFInteger) || value.is_a?(CFReal)) then
427
+ val = value.value
428
+ if(value.is_a?(CFInteger)) then
429
+ @int_size += Binary.bytes_int(val)
430
+ else
431
+ @misc_size += 9 # 9 bytes (8 + marker byte) for real
432
+ end
433
+
434
+ @count_objects += 1
435
+ return
436
+ elsif(value.is_a?(CFDate)) then
437
+ @misc_size += 9
438
+ @count_objects += 1
439
+ return
440
+ elsif(value.is_a?(CFBoolean)) then
441
+ @count_objects += 1
442
+ @misc_size += 1
443
+ return
444
+ elsif(value.is_a?(CFArray)) then
445
+ cnt = 0
446
+
447
+ value.value.each do |v|
448
+ cnt += 1
449
+ unique_and_count_values(v)
450
+ @object_refs += 1 # each array member is a ref
451
+ end
452
+
453
+ @count_objects += 1
454
+ @int_size += Binary.bytes_size_int(cnt)
455
+ @misc_size += 1 # marker byte for array
456
+ return
457
+ elsif(value.is_a?(CFDictionary)) then
458
+ cnt = 0
459
+
460
+ value.value.each_pair do |k,v|
461
+ cnt += 1
462
+
463
+ if(!@unique_table.has_key?(k))
464
+ @unique_table[k] = 0
465
+ @string_size += Binary.binary_strlen(k) + 1
466
+ @int_size += Binary.bytes_size_int(Binary.charset_strlen(k,'UTF-8'))
467
+ end
468
+
469
+ @object_refs += 2 # both, key and value, are refs
470
+ @unique_table[k] += 1
471
+ unique_and_count_values(v)
472
+ end
473
+
474
+ @count_objects += 1
475
+ @misc_size += 1 # marker byte for dict
476
+ @int_size += Binary.bytes_size_int(cnt)
477
+ return
478
+ elsif(value.is_a?(CFData)) then
479
+ val = value.decoded_value
480
+ @int_size += Binary.bytes_size_int(val.length)
481
+ @misc_size += val.length
482
+ @count_objects += 1
483
+ return
484
+ end
485
+
486
+ val = value.value
487
+ if(!@unique_table.has_key?(val)) then
488
+ @unique_table[val] = 0
489
+ @string_size += Binary.binary_strlen(val) + 1
490
+ @int_size += Binary.bytes_size_int(Binary.charset_strlen(val,'UTF-8'))
491
+ end
492
+
493
+ @unique_table[val] += 1
494
+ end
495
+ protected :unique_and_count_values
496
+
497
+ # Counts the number of bytes the string will have when coded; utf-16be if non-ascii characters are present.
498
+ def Binary.binary_strlen(val)
499
+ val.each_byte do |b|
500
+ if(b > 127) then
501
+ val = Binary.charset_convert(val, 'UTF-8', 'UTF-16BE')
502
+ return val.bytesize
503
+ end
504
+ end
505
+
506
+ return val.bytesize
507
+ end
508
+
509
+ # Uniques and transforms a string value to binary format and adds it to the object table
510
+ def string_to_binary(val)
511
+ saved_object_count = -1
512
+
513
+ unless(@unique_table.has_key?(val)) then
514
+ saved_object_count = @written_object_count
515
+ @written_object_count += 1
516
+
517
+ @unique_table[val] = saved_object_count
518
+ utf16 = false
519
+
520
+ val.each_byte do |b|
521
+ if(b > 127) then
522
+ utf16 = true
523
+ break
524
+ end
525
+ end
526
+
527
+ if(utf16) then
528
+ bdata = Binary.type_bytes("6",Binary.charset_strlen(val,"UTF-8")) # 6 is 0110, unicode string (utf16be)
529
+ val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
530
+
531
+ val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
532
+ @object_table[saved_object_count] = bdata + val
533
+ else
534
+ bdata = Binary.type_bytes("5",val.bytesize) # 5 is 0101 which is an ASCII string (seems to be ASCII encoded)
535
+ @object_table[saved_object_count] = bdata + val
536
+ end
537
+ else
538
+ saved_object_count = @unique_table[val]
539
+ end
540
+
541
+ return saved_object_count
542
+ end
543
+
544
+ # Codes an integer to binary format
545
+ def int_to_binary(value)
546
+ nbytes = 0
547
+ nbytes = 1 if value > 0xFF # 1 byte integer
548
+ nbytes += 1 if value > 0xFFFF # 4 byte integer
549
+ nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
550
+ nbytes = 3 if value < 0 # 8 byte integer, since signed
551
+
552
+ bdata = Binary.type_bytes("1", nbytes) # 1 is 0001, type indicator for integer
553
+ buff = ""
554
+
555
+ if(nbytes < 3) then
556
+ fmt = "N"
557
+
558
+ if(nbytes == 0) then
559
+ fmt = "C"
560
+ elsif(nbytes == 1)
561
+ fmt = "n"
562
+ end
563
+
564
+ buff = [value].pack(fmt)
565
+ else
566
+ # 64 bit signed integer; we need the higher and the lower 32 bit of the value
567
+ high_word = value >> 32
568
+ low_word = value & 0xFFFFFFFF
569
+ buff = [high_word,low_word].pack("NN")
570
+ end
571
+
572
+ return bdata + buff
573
+ end
574
+
575
+ # Codes a real value to binary format
576
+ def real_to_binary(val)
577
+ bdata = Binary.type_bytes("2",3) # 2 is 0010, type indicator for reals
578
+ buff = [val].pack("d")
579
+ return bdata + buff.reverse
580
+ end
581
+
582
+ # Converts a numeric value to binary and adds it to the object table
583
+ def num_to_binary(value)
584
+ saved_object_count = @written_object_count
585
+ @written_object_count += 1
586
+
587
+ val = ""
588
+ if(value.is_a?(CFInteger)) then
589
+ val = int_to_binary(value.value)
590
+ else
591
+ val = real_to_binary(value.value)
592
+ end
593
+
594
+ @object_table[saved_object_count] = val
595
+ return saved_object_count
596
+ end
597
+
598
+ # Convert date value (apple format) to binary and adds it to the object table
599
+ def date_to_binary(val)
600
+ saved_object_count = @written_object_count
601
+ @written_object_count += 1
602
+
603
+ 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
604
+
605
+ bdata = Binary.type_bytes("3", 3) # 3 is 0011, type indicator for date
606
+ @object_table[saved_object_count] = bdata + [val].pack("d").reverse
607
+
608
+ return saved_object_count
609
+ end
610
+
611
+ # Convert a bool value to binary and add it to the object table
612
+ def bool_to_binary(val)
613
+ saved_object_count = @written_object_count
614
+ @written_object_count += 1
615
+
616
+ @object_table[saved_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
617
+ return saved_object_count
618
+ end
619
+
620
+ # Convert data value to binary format and add it to the object table
621
+ def data_to_binary(val)
622
+ saved_object_count = @written_object_count
623
+ @written_object_count += 1
624
+
625
+ bdata = Binary.type_bytes("4", val.bytesize) # a is 1000, type indicator for data
626
+ @object_table[saved_object_count] = bdata + val
627
+
628
+ return saved_object_count
629
+ end
630
+
631
+ # Convert array to binary format and add it to the object table
632
+ def array_to_binary(val)
633
+ saved_object_count = @written_object_count
634
+ @written_object_count += 1
635
+
636
+ bdata = Binary.type_bytes("a", val.value.count) # a is 1010, type indicator for arrays
637
+
638
+ val.value.each do |v|
639
+ bdata += Binary.pack_it_with_size(@object_ref_size, v.to_binary(self));
640
+ end
641
+
642
+ @object_table[saved_object_count] = bdata
643
+ return saved_object_count
644
+ end
645
+
646
+ # Convert dictionary to binary format and add it to the object table
647
+ def dict_to_binary(val)
648
+ saved_object_count = @written_object_count
649
+ @written_object_count += 1
650
+
651
+ bdata = Binary.type_bytes("d",val.value.count) # d=1101, type indicator for dictionary
652
+
653
+ val.value.each_key do |k|
654
+ str = CFString.new(k)
655
+ key = str.to_binary(self)
656
+ bdata += Binary.pack_it_with_size(@object_ref_size,key)
657
+ end
658
+
659
+ val.value.each_value do |v|
660
+ bdata += Binary.pack_it_with_size(@object_ref_size,v.to_binary(self))
661
+ end
662
+
663
+ @object_table[saved_object_count] = bdata
664
+ return saved_object_count
665
+ end
666
+ end
667
+ end
668
+
669
+ # eof