CFPropertyList 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
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