1/*
2 * Copyright (C) 2011 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <unicode/uloc.h>
29#include <wtf/HashMap.h>
30#include <wtf/NeverDestroyed.h>
31#include <wtf/ThreadSpecific.h>
32#include <wtf/text/AtomString.h>
33
34namespace WTF {
35
36class LineBreakIteratorPool {
37 WTF_MAKE_NONCOPYABLE(LineBreakIteratorPool);
38 WTF_MAKE_FAST_ALLOCATED;
39public:
40 LineBreakIteratorPool() = default;
41
42 static LineBreakIteratorPool& sharedPool()
43 {
44 static NeverDestroyed<WTF::ThreadSpecific<LineBreakIteratorPool>> pool;
45 return *pool.get();
46 }
47
48 static AtomString makeLocaleWithBreakKeyword(const AtomString& locale, LineBreakIteratorMode mode)
49 {
50 // The uloc functions model locales as char*, so we have to downconvert our AtomString.
51 auto utf8Locale = locale.string().utf8();
52 if (!utf8Locale.length())
53 return locale;
54 Vector<char> scratchBuffer(utf8Locale.length() + 11, 0);
55 memcpy(scratchBuffer.data(), utf8Locale.data(), utf8Locale.length());
56
57 const char* keywordValue = nullptr;
58 switch (mode) {
59 case LineBreakIteratorMode::Default:
60 // nullptr will cause any existing values to be removed.
61 break;
62 case LineBreakIteratorMode::Loose:
63 keywordValue = "loose";
64 break;
65 case LineBreakIteratorMode::Normal:
66 keywordValue = "normal";
67 break;
68 case LineBreakIteratorMode::Strict:
69 keywordValue = "strict";
70 break;
71 }
72
73 UErrorCode status = U_ZERO_ERROR;
74 int32_t lengthNeeded = uloc_setKeywordValue("lb", keywordValue, scratchBuffer.data(), scratchBuffer.size(), &status);
75 if (U_SUCCESS(status))
76 return AtomString::fromUTF8(scratchBuffer.data(), lengthNeeded);
77 if (status == U_BUFFER_OVERFLOW_ERROR) {
78 scratchBuffer.grow(lengthNeeded + 1);
79 memset(scratchBuffer.data() + utf8Locale.length(), 0, scratchBuffer.size() - utf8Locale.length());
80 status = U_ZERO_ERROR;
81 int32_t lengthNeeded2 = uloc_setKeywordValue("lb", keywordValue, scratchBuffer.data(), scratchBuffer.size(), &status);
82 if (!U_SUCCESS(status) || lengthNeeded != lengthNeeded2)
83 return locale;
84 return AtomString::fromUTF8(scratchBuffer.data(), lengthNeeded);
85 }
86 return locale;
87 }
88
89 UBreakIterator* take(const AtomString& locale, LineBreakIteratorMode mode)
90 {
91 auto localeWithOptionalBreakKeyword = makeLocaleWithBreakKeyword(locale, mode);
92
93 UBreakIterator* iterator = nullptr;
94 for (size_t i = 0; i < m_pool.size(); ++i) {
95 if (m_pool[i].first == localeWithOptionalBreakKeyword) {
96 iterator = m_pool[i].second;
97 m_pool.remove(i);
98 break;
99 }
100 }
101
102 if (!iterator) {
103 iterator = openLineBreakIterator(localeWithOptionalBreakKeyword);
104 if (!iterator)
105 return nullptr;
106 }
107
108 ASSERT(!m_vendedIterators.contains(iterator));
109 m_vendedIterators.add(iterator, localeWithOptionalBreakKeyword);
110 return iterator;
111 }
112
113 void put(UBreakIterator* iterator)
114 {
115 ASSERT(m_vendedIterators.contains(iterator));
116 if (m_pool.size() == capacity) {
117 closeLineBreakIterator(m_pool[0].second);
118 m_pool.remove(0);
119 }
120 m_pool.uncheckedAppend({ m_vendedIterators.take(iterator), iterator });
121 }
122
123private:
124 static constexpr size_t capacity = 4;
125
126 Vector<std::pair<AtomString, UBreakIterator*>, capacity> m_pool;
127 HashMap<UBreakIterator*, AtomString> m_vendedIterators;
128
129 friend WTF::ThreadSpecific<LineBreakIteratorPool>::operator LineBreakIteratorPool*();
130};
131
132}
133