1/*
2 * Copyright (C) 2006, 2009, 2012 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include <wtf/text/StringImpl.h>
23
24#if USE(CF)
25
26#include <CoreFoundation/CoreFoundation.h>
27#include <wtf/MainThread.h>
28#include <wtf/PassRefPtr.h>
29#include <wtf/RetainPtr.h>
30#include <wtf/Threading.h>
31
32#if PLATFORM(MAC)
33#include <objc/objc-auto.h>
34#endif
35
36static inline bool garbageCollectionEnabled()
37{
38#if PLATFORM(MAC)
39    return objc_collectingEnabled();
40#else
41    return false;
42#endif
43}
44
45namespace WTF {
46
47namespace StringWrapperCFAllocator {
48
49    static StringImpl* currentString;
50
51    static const void* retain(const void* info)
52    {
53        return info;
54    }
55
56    NO_RETURN_DUE_TO_ASSERT
57    static void release(const void*)
58    {
59        ASSERT_NOT_REACHED();
60    }
61
62    static CFStringRef copyDescription(const void*)
63    {
64        return CFSTR("WTF::String-based allocator");
65    }
66
67    static void* allocate(CFIndex size, CFOptionFlags, void*)
68    {
69        StringImpl* underlyingString = 0;
70        if (isMainThread()) {
71            underlyingString = currentString;
72            if (underlyingString) {
73                currentString = 0;
74                underlyingString->ref(); // Balanced by call to deref in deallocate below.
75            }
76        }
77        StringImpl** header = static_cast<StringImpl**>(fastMalloc(sizeof(StringImpl*) + size));
78        *header = underlyingString;
79        return header + 1;
80    }
81
82    static void* reallocate(void* pointer, CFIndex newSize, CFOptionFlags, void*)
83    {
84        size_t newAllocationSize = sizeof(StringImpl*) + newSize;
85        StringImpl** header = static_cast<StringImpl**>(pointer) - 1;
86        ASSERT(!*header);
87        header = static_cast<StringImpl**>(fastRealloc(header, newAllocationSize));
88        return header + 1;
89    }
90
91    static void deallocateOnMainThread(void* headerPointer)
92    {
93        StringImpl** header = static_cast<StringImpl**>(headerPointer);
94        StringImpl* underlyingString = *header;
95        ASSERT(underlyingString);
96        underlyingString->deref(); // Balanced by call to ref in allocate above.
97        fastFree(header);
98    }
99
100    static void deallocate(void* pointer, void*)
101    {
102        StringImpl** header = static_cast<StringImpl**>(pointer) - 1;
103        StringImpl* underlyingString = *header;
104        if (!underlyingString)
105            fastFree(header);
106        else {
107            if (!isMainThread())
108                callOnMainThread(deallocateOnMainThread, header);
109            else {
110                underlyingString->deref(); // Balanced by call to ref in allocate above.
111                fastFree(header);
112            }
113        }
114    }
115
116    static CFIndex preferredSize(CFIndex size, CFOptionFlags, void*)
117    {
118        // FIXME: If FastMalloc provided a "good size" callback, we'd want to use it here.
119        // Note that this optimization would help performance for strings created with the
120        // allocator that are mutable, and those typically are only created by callers who
121        // make a new string using the old string's allocator, such as some of the call
122        // sites in CFURL.
123        return size;
124    }
125
126    static CFAllocatorRef create()
127    {
128        ASSERT(!garbageCollectionEnabled());
129        CFAllocatorContext context = { 0, 0, retain, release, copyDescription, allocate, reallocate, deallocate, preferredSize };
130        return CFAllocatorCreate(0, &context);
131    }
132
133    static CFAllocatorRef allocator()
134    {
135        static CFAllocatorRef allocator = create();
136        return allocator;
137    }
138
139}
140
141RetainPtr<CFStringRef> StringImpl::createCFString()
142{
143    // Since garbage collection isn't compatible with custom allocators, we
144    // can't use the NoCopy variants of CFStringCreate*() when GC is enabled.
145    if (!m_length || !isMainThread() || garbageCollectionEnabled()) {
146        if (is8Bit())
147            return adoptCF(CFStringCreateWithBytes(0, reinterpret_cast<const UInt8*>(characters8()), m_length, kCFStringEncodingISOLatin1, false));
148        return adoptCF(CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar*>(characters16()), m_length));
149    }
150    CFAllocatorRef allocator = StringWrapperCFAllocator::allocator();
151
152    // Put pointer to the StringImpl in a global so the allocator can store it with the CFString.
153    ASSERT(!StringWrapperCFAllocator::currentString);
154    StringWrapperCFAllocator::currentString = this;
155
156    CFStringRef string;
157    if (is8Bit())
158        string = CFStringCreateWithBytesNoCopy(allocator, reinterpret_cast<const UInt8*>(characters8()), m_length, kCFStringEncodingISOLatin1, false, kCFAllocatorNull);
159    else
160        string = CFStringCreateWithCharactersNoCopy(allocator, reinterpret_cast<const UniChar*>(characters16()), m_length, kCFAllocatorNull);
161    // CoreFoundation might not have to allocate anything, we clear currentString in case we did not execute allocate().
162    StringWrapperCFAllocator::currentString = 0;
163
164    return adoptCF(string);
165}
166
167// On StringImpl creation we could check if the allocator is the StringWrapperCFAllocator.
168// If it is, then we could find the original StringImpl and just return that. But to
169// do that we'd have to compute the offset from CFStringRef to the allocated block;
170// the CFStringRef is *not* at the start of an allocated block. Testing shows 1000x
171// more calls to createCFString than calls to the create functions with the appropriate
172// allocator, so it's probably not urgent optimize that case.
173
174}
175
176#endif // USE(CF)
177