Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include <svl/sharedstringpool.hxx>
11 : #include <svl/sharedstring.hxx>
12 : #include <unotools/charclass.hxx>
13 : #include <osl/mutex.hxx>
14 :
15 : #include <unordered_map>
16 : #include <unordered_set>
17 :
18 : namespace svl {
19 :
20 : namespace {
21 :
22 56 : inline sal_Int32 getRefCount( const rtl_uString* p )
23 : {
24 56 : return (p->refCount & 0x3FFFFFFF);
25 : }
26 :
27 : typedef std::unordered_set<OUString, OUStringHash> StrHashType;
28 : typedef std::pair<StrHashType::iterator, bool> InsertResultType;
29 : typedef std::unordered_map<const rtl_uString*, OUString> StrStoreType;
30 :
31 99112 : InsertResultType findOrInsert( StrHashType& rPool, const OUString& rStr )
32 : {
33 99112 : StrHashType::iterator it = rPool.find(rStr);
34 99112 : bool bInserted = false;
35 99112 : if (it == rPool.end())
36 : {
37 : // Not yet in the pool.
38 15763 : std::pair<StrHashType::iterator, bool> r = rPool.insert(rStr);
39 15763 : if (!r.second)
40 : // Insertion failed.
41 0 : return InsertResultType(rPool.end(), false);
42 :
43 15763 : it = r.first;
44 15763 : bInserted = true;
45 : }
46 :
47 99112 : return InsertResultType(it, bInserted);
48 : }
49 :
50 : }
51 :
52 2289 : struct SharedStringPool::Impl
53 : {
54 : mutable osl::Mutex maMutex;
55 : StrHashType maStrPool;
56 : StrHashType maStrPoolUpper;
57 : StrStoreType maStrStore;
58 : const CharClass* mpCharClass;
59 :
60 2322 : explicit Impl( const CharClass* pCharClass ) : mpCharClass(pCharClass) {}
61 : };
62 :
63 2322 : SharedStringPool::SharedStringPool( const CharClass* pCharClass ) :
64 2322 : mpImpl(new Impl(pCharClass)) {}
65 :
66 2289 : SharedStringPool::~SharedStringPool()
67 : {
68 2289 : delete mpImpl;
69 2289 : }
70 :
71 91189 : SharedString SharedStringPool::intern( const OUString& rStr )
72 : {
73 91189 : osl::MutexGuard aGuard(&mpImpl->maMutex);
74 :
75 91189 : InsertResultType aRes = findOrInsert(mpImpl->maStrPool, rStr);
76 91189 : if (aRes.first == mpImpl->maStrPool.end())
77 : // Insertion failed.
78 0 : return SharedString();
79 :
80 91189 : rtl_uString* pOrig = aRes.first->pData;
81 :
82 91189 : if (!mpImpl->mpCharClass)
83 : // We don't track case insensitive strings.
84 0 : return SharedString(pOrig, NULL);
85 :
86 91189 : if (!aRes.second)
87 : {
88 : // No new string has been inserted. Return the existing string in the pool.
89 83266 : StrStoreType::iterator it = mpImpl->maStrStore.find(pOrig);
90 83266 : if (it == mpImpl->maStrStore.end())
91 0 : return SharedString();
92 :
93 83266 : rtl_uString* pUpper = it->second.pData;
94 83266 : return SharedString(pOrig, pUpper);
95 : }
96 :
97 : // This is a new string insertion. Establish mapping to upper-case variant.
98 :
99 15846 : OUString aUpper = mpImpl->mpCharClass->uppercase(rStr);
100 7923 : aRes = findOrInsert(mpImpl->maStrPoolUpper, aUpper);
101 7923 : if (aRes.first == mpImpl->maStrPoolUpper.end())
102 : // Failed to insert or fetch upper-case variant. Should never happen.
103 0 : return SharedString();
104 :
105 7923 : mpImpl->maStrStore.insert(StrStoreType::value_type(pOrig, *aRes.first));
106 :
107 99112 : return SharedString(pOrig, aRes.first->pData);
108 : }
109 :
110 12 : void SharedStringPool::purge()
111 : {
112 12 : osl::MutexGuard aGuard(&mpImpl->maMutex);
113 :
114 24 : StrHashType aNewStrPool;
115 12 : StrHashType::iterator it = mpImpl->maStrPool.begin(), itEnd = mpImpl->maStrPool.end();
116 47 : for (; it != itEnd; ++it)
117 : {
118 35 : const rtl_uString* p = it->pData;
119 35 : if (getRefCount(p) == 1)
120 : {
121 : // Remove it from the upper string map. This should unref the
122 : // upper string linked to this original string.
123 11 : mpImpl->maStrStore.erase(p);
124 : }
125 : else
126 : // Still referenced outside the pool. Keep it.
127 24 : aNewStrPool.insert(*it);
128 : }
129 :
130 12 : mpImpl->maStrPool.swap(aNewStrPool);
131 :
132 12 : aNewStrPool.clear(); // for re-use.
133 :
134 : // Purge the upper string pool as well.
135 12 : it = mpImpl->maStrPoolUpper.begin();
136 12 : itEnd = mpImpl->maStrPoolUpper.end();
137 33 : for (; it != itEnd; ++it)
138 : {
139 21 : const rtl_uString* p = it->pData;
140 21 : if (getRefCount(p) > 1)
141 16 : aNewStrPool.insert(*it);
142 : }
143 :
144 24 : mpImpl->maStrPoolUpper.swap(aNewStrPool);
145 12 : }
146 :
147 14 : size_t SharedStringPool::getCount() const
148 : {
149 14 : osl::MutexGuard aGuard(&mpImpl->maMutex);
150 14 : return mpImpl->maStrPool.size();
151 : }
152 :
153 14 : size_t SharedStringPool::getCountIgnoreCase() const
154 : {
155 14 : osl::MutexGuard aGuard(&mpImpl->maMutex);
156 14 : return mpImpl->maStrPoolUpper.size();
157 : }
158 :
159 : }
160 :
161 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|