1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20//
21//  cfdata.m
22//  gctests
23//
24//  Created by Blaine Garst on 11/13/08.
25//  Copyright 2008 Apple. All rights reserved.
26//
27
28// CONFIG GC -C99
29
30
31#import <Foundation/Foundation.h>
32#import <CoreFoundation/CFData.h>
33#include <malloc/malloc.h>
34
35
36@interface TestObject : NSObject {
37    __strong CFDataRef  subject;
38    __strong CFMutableDataRef mutableSubject;
39
40    __strong CFDataRef subjectCopy;
41    __strong CFMutableDataRef mutableSubjectCopy;
42
43    __strong CFMutableDataRef subjectMutableCopy;
44    __strong CFMutableDataRef mutableSubjectMutableCopy;
45
46}
47
48- init;
49
50@end
51
52@interface TestTestObject : TestObject {
53    bool shouldDealloc;
54    __strong void *backingData;
55}
56@end
57
58
59@implementation TestObject
60- (void) makeCopies {
61    CFIndex size = 32*sizeof(void *);
62    subjectCopy         = CFDataCreateCopy(NULL, subject);
63    mutableSubjectCopy  = CFDataCreateMutableCopy(NULL, size, mutableSubject);
64
65    subjectMutableCopy          = CFDataCreateMutableCopy(NULL, size, subject);
66    mutableSubjectMutableCopy   = CFDataCreateMutableCopy(NULL, size, mutableSubject);
67}
68
69- (void)makeCollectable {
70    CFMakeCollectable(subjectCopy);
71    CFMakeCollectable(mutableSubjectCopy);
72    CFMakeCollectable(subjectMutableCopy);
73    CFMakeCollectable(mutableSubjectMutableCopy);
74    CFMakeCollectable(subject);
75    CFMakeCollectable(mutableSubject);
76}
77
78- (void)grow {
79    CFIndex size = 32*sizeof(void *);
80    CFDataSetLength(mutableSubjectCopy, 2*size);
81    CFDataSetLength(subjectMutableCopy, 2*size);
82    CFDataSetLength(mutableSubjectMutableCopy, 2*size);
83    CFDataSetLength(mutableSubject, 2*size);
84}
85
86- (void)dealloc {
87    CFRelease(subjectCopy);
88    CFRelease(mutableSubjectCopy);
89    CFRelease(subjectMutableCopy);
90    CFRelease(mutableSubjectMutableCopy);
91    CFRelease(subject);
92    CFRelease(mutableSubject);
93    [super dealloc];
94}
95
96- init {
97    id buffer[32];
98    for (int i = 0; i < 32; ++i)
99        buffer[i] = self;
100    CFIndex size = sizeof(void *)*32;
101    if (!subject) subject         = CFDataCreate(NULL, (uint8_t *)buffer, size);
102    mutableSubject  = CFDataCreateMutable(NULL, size);
103    const UInt8 *bytes  = CFDataGetBytePtr((CFMutableDataRef)mutableSubject);
104    memcpy((void *)bytes, buffer, size);
105
106    [self makeCopies];
107    [self makeCollectable];
108    [self grow];
109    return self;
110}
111
112
113bool allSame(void *value, void **items, int nitems) {
114    for (int i = 0; i < nitems; ++i)
115        if (items[i] != value) return false;
116    return true;
117}
118
119- (void)test {
120    bool result = true;
121    CFIndex count = CFDataGetLength(subject)/sizeof(void *);
122    result = result && allSame(self, (void **)CFDataGetBytePtr(subject), count);
123    result = result && allSame(self, (void **)CFDataGetBytePtr(mutableSubject), count);
124    result = result && allSame(self, (void **)CFDataGetBytePtr(subjectCopy), count);
125    result = result && allSame(self, (void **)CFDataGetBytePtr(mutableSubjectCopy), count);
126    result = result && allSame(self, (void **)CFDataGetBytePtr(subjectMutableCopy), count);
127    result = result && allSame(self, (void **)CFDataGetBytePtr(mutableSubjectMutableCopy), count);
128    if (!result) {
129        printf("Data changed!\n");
130        exit(1);
131    }
132}
133
134@end
135
136@implementation TestTestObject
137
138- (void)allocateBacking:(CFIndex)size {
139    backingData = malloc_zone_malloc((malloc_zone_t*)NSDefaultMallocZone(), size);
140}
141
142- init {
143    id buffer[32];
144    for (int i = 0; i < 32; ++i)
145        buffer[i] = self;
146    CFIndex size = sizeof(void *)*32;
147    [self allocateBacking:size];
148
149    memcpy(backingData, buffer, size);
150    subject         = CFDataCreateWithBytesNoCopy(NULL, (const UInt8*)backingData, size, NULL);
151    //if ([NSGarbageCollector defaultCollector]) printf("backing data %p held by %p\n", backingData, subject);
152    return [super init];
153}
154- initExplicit {
155    id buffer[32];
156    for (int i = 0; i < 32; ++i)
157        buffer[i] = self;
158    CFIndex size = sizeof(void *)*32;
159    [self allocateBacking:size];
160    memcpy(backingData, buffer, size);
161    // explicitly use kCFAllocatorDefault
162    subject         = CFDataCreateWithBytesNoCopy(NULL, (const UInt8*)backingData, size, kCFAllocatorDefault);
163    //if ([NSGarbageCollector defaultCollector]) printf("backing data %p held by %p\n", backingData, subject);
164    return [super init];
165}
166- initOwned {
167    id buffer[32];
168    for (int i = 0; i < 32; ++i)
169        buffer[i] = self;
170    CFIndex size = sizeof(void *)*32;
171    [self allocateBacking:size];
172    memcpy(backingData, buffer, size);
173    // explicitly use kCFAllocatorNull to say that the memory is owned elsewhere
174    subject         = CFDataCreateWithBytesNoCopy(NULL, (const UInt8*)backingData, size, kCFAllocatorNull);
175    shouldDealloc = true;
176    //if ([NSGarbageCollector defaultCollector]) printf("backing data %p held by %p, will dealloc\n", backingData, subject);
177    return [super init];
178}
179- (void) dealloc {
180    if (shouldDealloc) free(backingData);
181    [super dealloc];
182}
183- (void) finalize {
184    if (shouldDealloc) free(backingData);
185    [super finalize];
186}
187@end
188
189#define LOOPS 100
190
191void testLoop() {
192    NSMutableArray *array = [[NSMutableArray alloc] init];
193
194    for (int i = 0; i < LOOPS; ++i) {
195        TestObject *to = [[TestObject alloc] init];
196        [array addObject:to];
197        [to release];
198    }
199    for (int i = 0; i < LOOPS; ++i) {
200        TestTestObject *to = [[TestTestObject alloc] init];
201        [array addObject:to];
202        [to release];
203    }
204
205    for (int i = 0; i < LOOPS; ++i) {
206        TestTestObject *to = [[TestTestObject alloc] initExplicit];
207        [array addObject:to];
208        [to release];
209    }
210
211    for (int i = 0; i < LOOPS; ++i) {
212        TestTestObject *to = [[TestTestObject alloc] initOwned];
213        [array addObject:to];
214        [to release];
215    }
216
217    [array release];
218    NSGarbageCollector *collector = [NSGarbageCollector defaultCollector];
219    [collector collectIfNeeded];
220    [collector collectExhaustively];
221}
222
223
224int main(int argc, char *argv[]) {
225    NSAutoreleasePool *pool = [NSAutoreleasePool new];
226    for (int i = 0; i < (1+LOOPS); ++i) {   // just enough for GC to not find things on the stack
227        testLoop();
228        //printf("iteration %d done\n", i);
229    }
230    [pool drain];
231    printf("%s: Success\n", argv[0]);
232    return 0;
233}
234