Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svl/source/misc/inethist.cxx
Line
Count
Source
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
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <svl/inethist.hxx>
21
22
#include <algorithm>
23
#include <string.h>
24
25
#include <rtl/crc.h>
26
#include <tools/debug.hxx>
27
#include <tools/urlobj.hxx>
28
29
/*
30
 * INetURLHistory internals.
31
 */
32
25.9k
#define INETHIST_DEF_FTP_PORT    21
33
48.8k
#define INETHIST_DEF_HTTP_PORT   80
34
10.0k
#define INETHIST_DEF_HTTPS_PORT 443
35
36
532k
#define INETHIST_SIZE_LIMIT   1024
37
11
#define INETHIST_MAGIC_HEAD   0x484D4849UL
38
39
class INetURLHistory_Impl
40
{
41
    struct head_entry
42
    {
43
        /** Representation.
44
        */
45
        sal_uInt32 m_nMagic;
46
        sal_uInt16 m_nNext;
47
48
        /** Initialization.
49
        */
50
        void initialize()
51
11
        {
52
11
            m_nMagic = INETHIST_MAGIC_HEAD;
53
11
            m_nNext  = 0;
54
11
        }
55
    };
56
57
    struct hash_entry
58
    {
59
        /** Representation.
60
        */
61
        sal_uInt32 m_nHash;
62
        sal_uInt16 m_nLru;
63
64
        /** Initialization.
65
        */
66
        void initialize (sal_uInt16 nLru)
67
11.2k
        {
68
11.2k
            m_nHash = 0;
69
11.2k
            m_nLru  = nLru;
70
11.2k
        }
71
72
        /** Comparison.
73
        */
74
        bool operator== (sal_uInt32 nHash) const
75
1.90M
        {
76
1.90M
            return (m_nHash == nHash);
77
1.90M
        }
78
        bool operator< (sal_uInt32 nHash) const
79
1.72M
        {
80
1.72M
            return (m_nHash < nHash);
81
1.72M
        }
82
    };
83
84
    struct lru_entry
85
    {
86
        /** Representation.
87
        */
88
        sal_uInt32 m_nHash;
89
        sal_uInt16 m_nNext;
90
        sal_uInt16 m_nPrev;
91
92
        /** Initialization.
93
        */
94
        void initialize (sal_uInt16 nThis)
95
11.2k
        {
96
11.2k
            m_nHash = 0;
97
11.2k
            m_nNext = nThis;
98
11.2k
            m_nPrev = nThis;
99
11.2k
        }
100
    };
101
102
    /** Representation.
103
    */
104
    head_entry m_aHead;
105
    hash_entry m_pHash[INETHIST_SIZE_LIMIT];
106
    lru_entry  m_pList[INETHIST_SIZE_LIMIT];
107
108
    /** Initialization.
109
    */
110
    void initialize();
111
112
    static sal_uInt16 capacity()
113
532k
    {
114
532k
        return sal_uInt16(INETHIST_SIZE_LIMIT);
115
532k
    }
116
117
    static sal_uInt32 crc32 (OUString const & rData)
118
177k
    {
119
177k
        return rtl_crc32 (0, rData.getStr(), rData.getLength() * sizeof(sal_Unicode));
120
177k
    }
121
122
    sal_uInt16 find (sal_uInt32 nHash) const;
123
124
    void move (sal_uInt16 nSI, sal_uInt16 nDI);
125
126
    void backlink (sal_uInt16 nThis, sal_uInt16 nTail)
127
11.2k
    {
128
11.2k
        lru_entry &rThis = m_pList[nThis];
129
11.2k
        lru_entry &rTail = m_pList[nTail];
130
131
11.2k
        rTail.m_nNext = nThis;
132
11.2k
        rTail.m_nPrev = rThis.m_nPrev;
133
11.2k
        rThis.m_nPrev = nTail;
134
11.2k
        m_pList[rTail.m_nPrev].m_nNext = nTail;
135
11.2k
    }
136
137
    void unlink (sal_uInt16 nThis)
138
0
    {
139
0
        lru_entry &rThis = m_pList[nThis];
140
141
0
        m_pList[rThis.m_nPrev].m_nNext = rThis.m_nNext;
142
0
        m_pList[rThis.m_nNext].m_nPrev = rThis.m_nPrev;
143
0
        rThis.m_nNext = nThis;
144
0
        rThis.m_nPrev = nThis;
145
0
    }
146
147
public:
148
    INetURLHistory_Impl();
149
    INetURLHistory_Impl(const INetURLHistory_Impl&) = delete;
150
    INetURLHistory_Impl& operator=(const INetURLHistory_Impl&) = delete;
151
152
    /** putUrl/queryUrl.
153
    */
154
    void putUrl   (const OUString &rUrl);
155
    bool queryUrl (const OUString &rUrl) const;
156
};
157
158
INetURLHistory_Impl::INetURLHistory_Impl()
159
11
{
160
11
    initialize();
161
11
}
162
163
void INetURLHistory_Impl::initialize()
164
11
{
165
11
    m_aHead.initialize();
166
167
11
    sal_uInt16 i, n = capacity();
168
11.2k
    for (i = 0; i < n; i++)
169
11.2k
        m_pHash[i].initialize(i);
170
11.2k
    for (i = 0; i < n; i++)
171
11.2k
        m_pList[i].initialize(i);
172
11.2k
    for (i = 1; i < n; i++)
173
11.2k
        backlink (m_aHead.m_nNext, i);
174
11
}
175
176
sal_uInt16 INetURLHistory_Impl::find (sal_uInt32 nHash) const
177
177k
{
178
177k
    sal_uInt16 l = 0;
179
177k
    sal_uInt16 r = capacity() - 1;
180
177k
    sal_uInt16 c = capacity();
181
182
1.89M
    while ((l < r) && (r < c))
183
1.72M
    {
184
1.72M
        sal_uInt16 m = ((l + r) / 2) & 0xFFFF;
185
1.72M
        if (m_pHash[m] == nHash)
186
5.41k
            return m;
187
188
1.72M
        if (m_pHash[m] < nHash)
189
1.72M
            l = m + 1;
190
0
        else
191
0
            r = m - 1;
192
1.72M
    }
193
172k
    return l;
194
177k
}
195
196
void INetURLHistory_Impl::move (sal_uInt16 nSI, sal_uInt16 nDI)
197
0
{
198
0
    hash_entry e = m_pHash[nSI];
199
0
    if (nSI < nDI)
200
0
    {
201
        // shift left.
202
0
        memmove (
203
0
            &m_pHash[nSI    ],
204
0
            &m_pHash[nSI + 1],
205
0
            (nDI - nSI) * sizeof(hash_entry));
206
0
    }
207
0
    if (nSI > nDI)
208
0
    {
209
        // shift right.
210
0
        memmove (
211
0
            &m_pHash[nDI + 1],
212
0
            &m_pHash[nDI    ],
213
0
            (nSI - nDI) * sizeof(hash_entry));
214
0
    }
215
0
    m_pHash[nDI] = e;
216
0
}
217
218
void INetURLHistory_Impl::putUrl (const OUString &rUrl)
219
0
{
220
0
    sal_uInt32 h = crc32 (rUrl);
221
0
    sal_uInt16 k = find (h);
222
0
    if ((k < capacity()) && (m_pHash[k] == h))
223
0
    {
224
        // Cache hit.
225
0
        sal_uInt16 nMRU = m_pHash[k].m_nLru;
226
0
        if (nMRU != m_aHead.m_nNext)
227
0
        {
228
            // Update LRU chain.
229
0
            unlink (nMRU);
230
0
            backlink (m_aHead.m_nNext, nMRU);
231
232
            // Rotate LRU chain.
233
0
            m_aHead.m_nNext = m_pList[m_aHead.m_nNext].m_nPrev;
234
0
        }
235
0
    }
236
0
    else
237
0
    {
238
        // Cache miss. Obtain least recently used.
239
0
        sal_uInt16 nLRU = m_pList[m_aHead.m_nNext].m_nPrev;
240
241
0
        sal_uInt16 nSI = find (m_pList[nLRU].m_nHash);
242
0
        if (nLRU != m_pHash[nSI].m_nLru)
243
0
        {
244
            // Update LRU chain.
245
0
            nLRU = m_pHash[nSI].m_nLru;
246
0
            unlink (nLRU);
247
0
            backlink (m_aHead.m_nNext, nLRU);
248
0
        }
249
250
        // Rotate LRU chain.
251
0
        m_aHead.m_nNext = m_pList[m_aHead.m_nNext].m_nPrev;
252
253
        // Check source and destination.
254
0
        sal_uInt16 nDI = std::min (k, sal_uInt16(capacity() - 1));
255
0
        if (nSI < nDI && !(m_pHash[nDI] < h))
256
0
            nDI -= 1;
257
0
        if (nDI < nSI && m_pHash[nDI] < h)
258
0
            nDI += 1;
259
260
        // Assign data.
261
0
        m_pList[m_aHead.m_nNext].m_nHash = m_pHash[nSI].m_nHash = h;
262
0
        move (nSI, nDI);
263
0
    }
264
0
}
265
266
bool INetURLHistory_Impl::queryUrl (const OUString &rUrl) const
267
177k
{
268
177k
    sal_uInt32 h = crc32 (rUrl);
269
177k
    sal_uInt16 k = find (h);
270
    // true if cache hit
271
177k
    return (k < capacity()) && (m_pHash[k] == h);
272
177k
}
273
274
11
INetURLHistory::INetURLHistory() : m_pImpl (new INetURLHistory_Impl())
275
11
{
276
11
}
277
278
INetURLHistory::~INetURLHistory()
279
11
{
280
11
}
281
282
/*
283
 * GetOrCreate.
284
 */
285
INetURLHistory* INetURLHistory::GetOrCreate()
286
1.15M
{
287
1.15M
    static INetURLHistory instance;
288
1.15M
    return &instance;
289
1.15M
}
290
291
void INetURLHistory::NormalizeUrl_Impl (INetURLObject &rUrl)
292
177k
{
293
177k
    switch (rUrl.GetProtocol())
294
177k
    {
295
85.1k
        case INetProtocol::File:
296
85.1k
            if (!INetURLObject::IsCaseSensitive())
297
0
            {
298
0
                OUString aPath (rUrl.GetURLPath(INetURLObject::DecodeMechanism::NONE).toAsciiLowerCase());
299
0
                rUrl.SetURLPath (aPath, INetURLObject::EncodeMechanism::NotCanonical);
300
0
            }
301
85.1k
            break;
302
303
26.6k
        case INetProtocol::Ftp:
304
26.6k
            if (!rUrl.HasPort())
305
25.9k
                rUrl.SetPort (INETHIST_DEF_FTP_PORT);
306
26.6k
            break;
307
308
50.2k
        case INetProtocol::Http:
309
50.2k
            if (!rUrl.HasPort())
310
48.8k
                rUrl.SetPort (INETHIST_DEF_HTTP_PORT);
311
50.2k
            if (!rUrl.HasURLPath())
312
0
                rUrl.SetURLPath(u"/");
313
50.2k
            break;
314
315
10.2k
        case INetProtocol::Https:
316
10.2k
            if (!rUrl.HasPort())
317
10.0k
                rUrl.SetPort (INETHIST_DEF_HTTPS_PORT);
318
10.2k
            if (!rUrl.HasURLPath())
319
0
                rUrl.SetURLPath(u"/");
320
10.2k
            break;
321
322
5.41k
        default:
323
5.41k
            break;
324
177k
    }
325
177k
}
326
327
void INetURLHistory::PutUrl_Impl (const INetURLObject &rUrl)
328
0
{
329
0
    DBG_ASSERT (m_pImpl, "PutUrl_Impl(): no Implementation");
330
0
    if (!m_pImpl)
331
0
        return;
332
333
0
    INetURLObject aHistUrl (rUrl);
334
0
    NormalizeUrl_Impl (aHistUrl);
335
336
0
    m_pImpl->putUrl (aHistUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE));
337
0
    Broadcast (INetURLHistoryHint (&rUrl));
338
339
0
    if (aHistUrl.HasMark())
340
0
    {
341
0
        aHistUrl.SetURL (aHistUrl.GetURLNoMark(INetURLObject::DecodeMechanism::NONE),
342
0
                         INetURLObject::EncodeMechanism::NotCanonical);
343
344
0
        m_pImpl->putUrl (aHistUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE));
345
0
        Broadcast (INetURLHistoryHint (&aHistUrl));
346
0
    }
347
0
}
348
349
bool INetURLHistory::QueryUrl(std::u16string_view rUrl) const
350
1.14M
{
351
1.14M
    INetProtocol eProto = INetURLObject::CompareProtocolScheme (rUrl);
352
1.14M
    if (!QueryProtocol (eProto))
353
972k
        return false;
354
177k
    return QueryUrl_Impl( INetURLObject(rUrl) );
355
1.14M
}
356
357
358
bool INetURLHistory::QueryUrl_Impl (INetURLObject rUrl) const
359
177k
{
360
177k
    DBG_ASSERT (m_pImpl, "QueryUrl_Impl(): no Implementation");
361
177k
    if (m_pImpl)
362
177k
    {
363
177k
        NormalizeUrl_Impl (rUrl);
364
365
177k
        return m_pImpl->queryUrl (rUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE));
366
177k
    }
367
0
    return false;
368
177k
}
369
370
371
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */