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 "wtf/MainThread.h"
27#include "wtf/PassRefPtr.h"
28#include "wtf/RetainPtr.h"
29#include "wtf/Threading.h"
30#include <CoreFoundation/CoreFoundation.h>
31
32namespace WTF {
33
34namespace StringWrapperCFAllocator {
35
36static StringImpl* currentString;
37
38static const void* retain(const void* info)
39{
40    return info;
41}
42
43NO_RETURN_DUE_TO_ASSERT
44static void release(const void*)
45{
46    ASSERT_NOT_REACHED();
47}
48
49static CFStringRef copyDescription(const void*)
50{
51    return CFSTR("WTF::String-based allocator");
52}
53
54static void* allocate(CFIndex size, CFOptionFlags, void*)
55{
56    StringImpl* underlyingString = 0;
57    if (isMainThread()) {
58        underlyingString = currentString;
59        if (underlyingString) {
60            currentString = 0;
61            underlyingString->ref(); // Balanced by call to deref in deallocate below.
62        }
63    }
64    StringImpl** header = static_cast<StringImpl**>(fastMalloc(sizeof(StringImpl*) + size));
65    *header = underlyingString;
66    return header + 1;
67}
68
69static void* reallocate(void* pointer, CFIndex newSize, CFOptionFlags, void*)
70{
71    size_t newAllocationSize = sizeof(StringImpl*) + newSize;
72    StringImpl** header = static_cast<StringImpl**>(pointer) - 1;
73    ASSERT(!*header);
74    header = static_cast<StringImpl**>(fastRealloc(header, newAllocationSize));
75    return header + 1;
76}
77
78static void deallocateOnMainThread(void* headerPointer)
79{
80    StringImpl** header = static_cast<StringImpl**>(headerPointer);
81    StringImpl* underlyingString = *header;
82    ASSERT(underlyingString);
83    underlyingString->deref(); // Balanced by call to ref in allocate above.
84    fastFree(header);
85}
86
87static void deallocate(void* pointer, void*)
88{
89    StringImpl** header = static_cast<StringImpl**>(pointer) - 1;
90    StringImpl* underlyingString = *header;
91    if (!underlyingString) {
92        fastFree(header);
93    } else {
94        if (!isMainThread()) {
95            callOnMainThread(deallocateOnMainThread, header);
96        } else {
97            underlyingString->deref(); // Balanced by call to ref in allocate above.
98            fastFree(header);
99        }
100    }
101}
102
103static CFIndex preferredSize(CFIndex size, CFOptionFlags, void*)
104{
105    // FIXME: If FastMalloc provided a "good size" callback, we'd want to use it here.
106    // Note that this optimization would help performance for strings created with the
107    // allocator that are mutable, and those typically are only created by callers who
108    // make a new string using the old string's allocator, such as some of the call
109    // sites in CFURL.
110    return size;
111}
112
113static CFAllocatorRef create()
114{
115    CFAllocatorContext context = { 0, 0, retain, release, copyDescription, allocate, reallocate, deallocate, preferredSize };
116    return CFAllocatorCreate(0, &context);
117}
118
119static CFAllocatorRef allocator()
120{
121    static CFAllocatorRef allocator = create();
122    return allocator;
123}
124
125}
126
127RetainPtr<CFStringRef> StringImpl::createCFString()
128{
129    // Since garbage collection isn't compatible with custom allocators, we
130    // can't use the NoCopy variants of CFStringCreate*() when GC is enabled.
131    if (!m_length || !isMainThread()) {
132        if (is8Bit())
133            return adoptCF(CFStringCreateWithBytes(0, reinterpret_cast<const UInt8*>(characters8()), m_length, kCFStringEncodingISOLatin1, false));
134        return adoptCF(CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar*>(characters16()), m_length));
135    }
136    CFAllocatorRef allocator = StringWrapperCFAllocator::allocator();
137
138    // Put pointer to the StringImpl in a global so the allocator can store it with the CFString.
139    ASSERT(!StringWrapperCFAllocator::currentString);
140    StringWrapperCFAllocator::currentString = this;
141
142    CFStringRef string;
143    if (is8Bit())
144        string = CFStringCreateWithBytesNoCopy(allocator, reinterpret_cast<const UInt8*>(characters8()), m_length, kCFStringEncodingISOLatin1, false, kCFAllocatorNull);
145    else
146        string = CFStringCreateWithCharactersNoCopy(allocator, reinterpret_cast<const UniChar*>(characters16()), m_length, kCFAllocatorNull);
147    // CoreFoundation might not have to allocate anything, we clear currentString in case we did not execute allocate().
148    StringWrapperCFAllocator::currentString = 0;
149
150    return adoptCF(string);
151}
152
153// On StringImpl creation we could check if the allocator is the StringWrapperCFAllocator.
154// If it is, then we could find the original StringImpl and just return that. But to
155// do that we'd have to compute the offset from CFStringRef to the allocated block;
156// the CFStringRef is *not* at the start of an allocated block. Testing shows 1000x
157// more calls to createCFString than calls to the create functions with the appropriate
158// allocator, so it's probably not urgent optimize that case.
159
160}
161
162#endif // USE(CF)
163