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);
38public:
39 LineBreakIteratorPool() = default;
40
41 static LineBreakIteratorPool& sharedPool()
42 {
43 static NeverDestroyed<WTF::ThreadSpecific<LineBreakIteratorPool>> pool;
44 return *pool.get();
45 }
46
47 static AtomString makeLocaleWithBreakKeyword(const AtomString& locale, LineBreakIteratorMode mode)
48 {
49 // The uloc functions model locales as char*, so we have to downconvert our AtomString.
50 auto utf8Locale = locale.string().utf8();
51 if (!utf8Locale.length())
52 return locale;
53 Vector<char> scratchBuffer(utf8Locale.length() + 11, 0);
54 memcpy(scratchBuffer.data(), utf8Locale.data(), utf8Locale.length());
55
56 const char* keywordValue = nullptr;
57 switch (mode) {
58 case LineBreakIteratorMode::Default:
59 // nullptr will cause any existing values to be removed.
60 break;
61 case LineBreakIteratorMode::Loose:
62 keywordValue = "loose";
63 break;
64 case LineBreakIteratorMode::Normal:
65 keywordValue = "normal";
66 break;
67 case LineBreakIteratorMode::Strict:
68 keywordValue = "strict";
69 break;
70 }
71
72 UErrorCode status = U_ZERO_ERROR;
73 int32_t lengthNeeded = uloc_setKeywordValue("lb", keywordValue, scratchBuffer.data(), scratchBuffer.size(), &status);
74 if (U_SUCCESS(status))
75 return AtomString::fromUTF8(scratchBuffer.data(), lengthNeeded);
76 if (status == U_BUFFER_OVERFLOW_ERROR) {
77 scratchBuffer.grow(lengthNeeded + 1);
78 memset(scratchBuffer.data() + utf8Locale.length(), 0, scratchBuffer.size() - utf8Locale.length());
79 status = U_ZERO_ERROR;
80 int32_t lengthNeeded2 = uloc_setKeywordValue("lb", keywordValue, scratchBuffer.data(), scratchBuffer.size(), &status);
81 if (!U_SUCCESS(status) || lengthNeeded != lengthNeeded2)
82 return locale;
83 return AtomString::fromUTF8(scratchBuffer.data(), lengthNeeded);
84 }
85 return locale;
86 }
87
88 UBreakIterator* take(const AtomString& locale, LineBreakIteratorMode mode)
89 {
90 auto localeWithOptionalBreakKeyword = makeLocaleWithBreakKeyword(locale, mode);
91
92 UBreakIterator* iterator = nullptr;
93 for (size_t i = 0; i < m_pool.size(); ++i) {
94 if (m_pool[i].first == localeWithOptionalBreakKeyword) {
95 iterator = m_pool[i].second;
96 m_pool.remove(i);
97 break;
98 }
99 }
100
101 if (!iterator) {
102 iterator = openLineBreakIterator(localeWithOptionalBreakKeyword);
103 if (!iterator)
104 return nullptr;
105 }
106
107 ASSERT(!m_vendedIterators.contains(iterator));
108 m_vendedIterators.add(iterator, localeWithOptionalBreakKeyword);
109 return iterator;
110 }
111
112 void put(UBreakIterator* iterator)
113 {
114 ASSERT(m_vendedIterators.contains(iterator));
115 if (m_pool.size() == capacity) {
116 closeLineBreakIterator(m_pool[0].second);
117 m_pool.remove(0);
118 }
119 m_pool.uncheckedAppend({ m_vendedIterators.take(iterator), iterator });
120 }
121
122private:
123 static constexpr size_t capacity = 4;
124
125 Vector<std::pair<AtomString, UBreakIterator*>, capacity> m_pool;
126 HashMap<UBreakIterator*, AtomString> m_vendedIterators;
127
128 friend WTF::ThreadSpecific<LineBreakIteratorPool>::operator LineBreakIteratorPool*();
129};
130
131}
132