Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cplstringlist.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  CPLStringList implementation.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2011, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "cpl_string.h"
16
17
#include <cstddef>
18
#include <cstdio>
19
#include <cstdlib>
20
#include <cstring>
21
22
#include <algorithm>
23
#include <limits>
24
#include <string>
25
26
#include "cpl_conv.h"
27
#include "cpl_error.h"
28
29
static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb);
30
31
/************************************************************************/
32
/*                           CPLStringList()                            */
33
/************************************************************************/
34
35
459M
CPLStringList::CPLStringList() = default;
36
37
/************************************************************************/
38
/*                           CPLStringList()                            */
39
/************************************************************************/
40
41
/**
42
 * CPLStringList constructor.
43
 *
44
 * @param papszListIn the NULL terminated list of strings to consume.
45
 * @param bTakeOwnership TRUE if the CPLStringList should take ownership
46
 * of the list of strings which implies responsibility to free them.
47
 */
48
49
CPLStringList::CPLStringList(char **papszListIn, int bTakeOwnership)
50
13.4M
    : CPLStringList()
51
52
13.4M
{
53
13.4M
    Assign(papszListIn, bTakeOwnership);
54
13.4M
}
55
56
/************************************************************************/
57
/*                           CPLStringList()                            */
58
/************************************************************************/
59
60
/**
61
 * CPLStringList constructor.
62
 *
63
 * The input list is copied.
64
 *
65
 * @param papszListIn the NULL terminated list of strings to ingest.
66
 */
67
68
108k
CPLStringList::CPLStringList(CSLConstList papszListIn) : CPLStringList()
69
70
108k
{
71
108k
    Assign(CSLDuplicate(papszListIn));
72
108k
}
73
74
/************************************************************************/
75
/*                           CPLStringList()                            */
76
/************************************************************************/
77
78
/**
79
 * CPLStringList constructor.
80
 *
81
 * The input list is copied.
82
 *
83
 * @param aosList input list.
84
 *
85
 * @since GDAL 3.9
86
 */
87
CPLStringList::CPLStringList(const std::vector<std::string> &aosList)
88
0
{
89
0
    if (!aosList.empty())
90
0
    {
91
0
        bOwnList = true;
92
0
        papszList = static_cast<char **>(
93
0
            VSI_CALLOC_VERBOSE(aosList.size() + 1, sizeof(char *)));
94
0
        nCount = static_cast<int>(aosList.size());
95
0
        for (int i = 0; i < nCount; ++i)
96
0
        {
97
0
            papszList[i] = VSI_STRDUP_VERBOSE(aosList[i].c_str());
98
0
        }
99
0
    }
100
0
}
101
102
/************************************************************************/
103
/*                           CPLStringList()                            */
104
/************************************************************************/
105
106
/**
107
 * CPLStringList constructor.
108
 *
109
 * The input list is copied.
110
 *
111
 * @param oInitList input list.
112
 *
113
 * @since GDAL 3.9
114
 */
115
CPLStringList::CPLStringList(std::initializer_list<const char *> oInitList)
116
0
{
117
0
    for (const char *pszStr : oInitList)
118
0
    {
119
0
        AddString(pszStr);
120
0
    }
121
0
}
122
123
/************************************************************************/
124
/*                           CPLStringList()                            */
125
/************************************************************************/
126
127
//! Copy constructor
128
464k
CPLStringList::CPLStringList(const CPLStringList &oOther) : CPLStringList()
129
130
464k
{
131
464k
    operator=(oOther);
132
464k
}
133
134
/************************************************************************/
135
/*                           CPLStringList()                            */
136
/************************************************************************/
137
138
//! Move constructor
139
108M
CPLStringList::CPLStringList(CPLStringList &&oOther) : CPLStringList()
140
141
108M
{
142
108M
    operator=(std::move(oOther));
143
108M
}
144
145
/************************************************************************/
146
/*                          BoundToConstList()                          */
147
/************************************************************************/
148
149
/**
150
 * Return a CPLStringList that wraps the passed list.
151
 *
152
 * The input list is *NOT* copied and must be kept alive while the
153
 * return CPLStringList is used.
154
 *
155
 * @param papszListIn a NULL terminated list of strings to wrap into the CPLStringList
156
 * @since GDAL 3.9
157
 */
158
159
/* static */
160
const CPLStringList CPLStringList::BoundToConstList(CSLConstList papszListIn)
161
0
{
162
0
    return CPLStringList(const_cast<char **>(papszListIn),
163
0
                         /* bTakeOwnership= */ false);
164
0
}
165
166
/************************************************************************/
167
/*                             operator=()                              */
168
/************************************************************************/
169
170
CPLStringList &CPLStringList::operator=(const CPLStringList &oOther)
171
930k
{
172
930k
    if (this != &oOther)
173
930k
    {
174
930k
        char **l_papszList = CSLDuplicate(oOther.papszList);
175
930k
        if (l_papszList)
176
24.6k
        {
177
24.6k
            Assign(l_papszList, TRUE);
178
24.6k
            nAllocation = oOther.nCount > 0 ? oOther.nCount + 1 : 0;
179
24.6k
            nCount = oOther.nCount;
180
24.6k
            bIsSorted = oOther.bIsSorted;
181
24.6k
        }
182
930k
    }
183
184
930k
    return *this;
185
930k
}
186
187
/************************************************************************/
188
/*                             operator=()                              */
189
/************************************************************************/
190
191
CPLStringList &CPLStringList::operator=(CPLStringList &&oOther)
192
108M
{
193
108M
    if (this != &oOther)
194
108M
    {
195
108M
        Clear();
196
108M
        papszList = oOther.papszList;
197
108M
        oOther.papszList = nullptr;
198
108M
        nCount = oOther.nCount;
199
108M
        oOther.nCount = 0;
200
108M
        nAllocation = oOther.nAllocation;
201
108M
        oOther.nAllocation = 0;
202
108M
        bOwnList = oOther.bOwnList;
203
108M
        oOther.bOwnList = false;
204
108M
        bIsSorted = oOther.bIsSorted;
205
108M
        oOther.bIsSorted = true;
206
108M
    }
207
208
108M
    return *this;
209
108M
}
210
211
/************************************************************************/
212
/*                             operator=()                              */
213
/************************************************************************/
214
215
CPLStringList &CPLStringList::operator=(CSLConstList papszListIn)
216
670k
{
217
670k
    if (papszListIn != papszList)
218
111k
    {
219
111k
        Assign(CSLDuplicate(papszListIn));
220
111k
        bIsSorted = false;
221
111k
    }
222
223
670k
    return *this;
224
670k
}
225
226
/************************************************************************/
227
/*                           ~CPLStringList()                           */
228
/************************************************************************/
229
230
CPLStringList::~CPLStringList()
231
232
459M
{
233
459M
    Clear();
234
459M
}
235
236
/************************************************************************/
237
/*                               Clear()                                */
238
/************************************************************************/
239
240
/**
241
 * Clear the string list.
242
 */
243
CPLStringList &CPLStringList::Clear()
244
245
624M
{
246
624M
    if (bOwnList)
247
136M
    {
248
136M
        CSLDestroy(papszList);
249
136M
        papszList = nullptr;
250
251
136M
        bOwnList = FALSE;
252
136M
        nAllocation = 0;
253
136M
        nCount = 0;
254
136M
    }
255
256
624M
    return *this;
257
624M
}
258
259
/************************************************************************/
260
/*                               Assign()                               */
261
/************************************************************************/
262
263
/**
264
 * Assign a list of strings.
265
 *
266
 *
267
 * @param papszListIn the NULL terminated list of strings to consume.
268
 * @param bTakeOwnership TRUE if the CPLStringList should take ownership
269
 * of the list of strings which implies responsibility to free them.
270
 *
271
 * @return a reference to the CPLStringList on which it was invoked.
272
 */
273
274
CPLStringList &CPLStringList::Assign(char **papszListIn, int bTakeOwnership)
275
276
44.1M
{
277
44.1M
    Clear();
278
279
44.1M
    papszList = papszListIn;
280
44.1M
    bOwnList = CPL_TO_BOOL(bTakeOwnership);
281
282
44.1M
    if (papszList == nullptr || *papszList == nullptr)
283
22.1M
        nCount = 0;
284
22.0M
    else
285
22.0M
        nCount = -1;  // unknown
286
287
44.1M
    nAllocation = 0;
288
44.1M
    bIsSorted = FALSE;
289
290
44.1M
    return *this;
291
44.1M
}
292
293
/************************************************************************/
294
/*                               Count()                                */
295
/************************************************************************/
296
297
/**
298
 * @return count of strings in the list, zero if empty.
299
 */
300
301
int CPLStringList::Count() const
302
303
131M
{
304
131M
    if (nCount == -1)
305
21.6M
    {
306
21.6M
        if (papszList == nullptr)
307
0
        {
308
0
            nCount = 0;
309
0
            nAllocation = 0;
310
0
        }
311
21.6M
        else
312
21.6M
        {
313
21.6M
            nCount = CSLCount(papszList);
314
21.6M
            nAllocation = std::max(nCount + 1, nAllocation);
315
21.6M
        }
316
21.6M
    }
317
318
131M
    return nCount;
319
131M
}
320
321
/************************************************************************/
322
/*                           MakeOurOwnCopy()                           */
323
/*                                                                      */
324
/*      If we don't own the list, a copy is made which we own.          */
325
/*      Necessary if we are going to modify the list.                   */
326
/************************************************************************/
327
328
bool CPLStringList::MakeOurOwnCopy()
329
330
255M
{
331
255M
    if (bOwnList)
332
6.46M
        return true;
333
334
249M
    if (papszList == nullptr)
335
249M
        return true;
336
337
0
    Count();
338
0
    char **papszListNew = CSLDuplicate(papszList);
339
0
    if (papszListNew == nullptr)
340
0
    {
341
0
        return false;
342
0
    }
343
0
    papszList = papszListNew;
344
0
    bOwnList = true;
345
0
    nAllocation = nCount + 1;
346
0
    return true;
347
0
}
348
349
/************************************************************************/
350
/*                          EnsureAllocation()                          */
351
/*                                                                      */
352
/*      Ensure we have enough room allocated for at least the           */
353
/*      requested number of strings (so nAllocation will be at least    */
354
/*      one more than the target)                                       */
355
/************************************************************************/
356
357
bool CPLStringList::EnsureAllocation(int nMaxList)
358
359
380M
{
360
380M
    if (!bOwnList)
361
194M
    {
362
194M
        if (!MakeOurOwnCopy())
363
0
            return false;
364
194M
    }
365
366
380M
    if (papszList == nullptr || nAllocation <= nMaxList)
367
195M
    {
368
        // we need to be able to store nMaxList+1 as an int,
369
        // and allocate (nMaxList+1) * sizeof(char*) bytes
370
195M
        if (nMaxList < 0 || nMaxList > std::numeric_limits<int>::max() - 1 ||
371
195M
            static_cast<size_t>(nMaxList) >
372
195M
                std::numeric_limits<size_t>::max() / sizeof(char *) - 1)
373
0
        {
374
0
            return false;
375
0
        }
376
195M
        int nNewAllocation = nMaxList + 1;
377
195M
        if (nNewAllocation <= (std::numeric_limits<int>::max() - 20) / 2 /
378
195M
                                  static_cast<int>(sizeof(char *)))
379
195M
            nNewAllocation = std::max(nNewAllocation * 2 + 20, nMaxList + 1);
380
195M
        if (papszList == nullptr)
381
194M
        {
382
194M
            papszList = static_cast<char **>(
383
194M
                VSI_CALLOC_VERBOSE(nNewAllocation, sizeof(char *)));
384
194M
            bOwnList = true;
385
194M
            nCount = 0;
386
194M
            if (papszList == nullptr)
387
0
                return false;
388
194M
        }
389
963k
        else
390
963k
        {
391
963k
            char **papszListNew = static_cast<char **>(VSI_REALLOC_VERBOSE(
392
963k
                papszList, nNewAllocation * sizeof(char *)));
393
963k
            if (papszListNew == nullptr)
394
0
                return false;
395
963k
            papszList = papszListNew;
396
963k
        }
397
195M
        nAllocation = nNewAllocation;
398
195M
    }
399
380M
    return true;
400
380M
}
401
402
/************************************************************************/
403
/*                         AddStringDirectly()                          */
404
/************************************************************************/
405
406
/**
407
 * Add a string to the list.
408
 *
409
 * This method is similar to AddString(), but ownership of the
410
 * pszNewString is transferred to the CPLStringList class.
411
 *
412
 * @param pszNewString the string to add to the list.
413
 */
414
415
CPLStringList &CPLStringList::AddStringDirectly(char *pszNewString)
416
417
380M
{
418
380M
    if (nCount == -1)
419
17.3k
        Count();
420
421
380M
    if (!EnsureAllocation(nCount + 1))
422
0
    {
423
0
        VSIFree(pszNewString);
424
0
        return *this;
425
0
    }
426
427
380M
    papszList[nCount++] = pszNewString;
428
380M
    papszList[nCount] = nullptr;
429
430
380M
    bIsSorted = false;
431
432
380M
    return *this;
433
380M
}
434
435
/************************************************************************/
436
/*                             AddString()                              */
437
/************************************************************************/
438
439
/**
440
 * Add a string to the list.
441
 *
442
 * A copy of the passed in string is made and inserted in the list.
443
 *
444
 * @param pszNewString the string to add to the list.
445
 */
446
447
CPLStringList &CPLStringList::AddString(const char *pszNewString)
448
449
314M
{
450
314M
    char *pszDupString = VSI_STRDUP_VERBOSE(pszNewString);
451
314M
    if (pszDupString == nullptr)
452
0
        return *this;
453
314M
    return AddStringDirectly(pszDupString);
454
314M
}
455
456
/************************************************************************/
457
/*                             AddString()                              */
458
/************************************************************************/
459
/**
460
 * Add a string to the list.
461
 *
462
 * A copy of the passed in string is made and inserted in the list.
463
 *
464
 * @param newString the string to add to the list.
465
 * @return a reference to the CPLStringList on which it was invoked.
466
 */
467
468
CPLStringList &CPLStringList::AddString(const std::string &newString)
469
1.07M
{
470
1.07M
    return AddString(newString.c_str());
471
1.07M
}
472
473
/************************************************************************/
474
/*                             push_back()                              */
475
/************************************************************************/
476
477
/**
478
 * Add a string to the list.
479
 *
480
 * A copy of the passed in string is made and inserted in the list.
481
 *
482
 * @param svStr the string to add to the list.
483
 *
484
 * @since 3.13
485
 */
486
487
void CPLStringList::push_back(std::string_view svStr)
488
489
0
{
490
0
    char *pszDupString =
491
0
        static_cast<char *>(VSI_MALLOC_VERBOSE(svStr.size() + 1));
492
0
    if (pszDupString == nullptr)
493
0
        return;
494
0
    memcpy(pszDupString, svStr.data(), svStr.size());
495
0
    pszDupString[svStr.size()] = 0;
496
0
    CPL_IGNORE_RET_VAL(AddStringDirectly(pszDupString));
497
0
}
498
499
/************************************************************************/
500
/*                            AddNameValue()                            */
501
/************************************************************************/
502
503
/**
504
 * Add a name=value entry to the list.
505
 *
506
 * A key=value string is prepared and appended to the list.  There is no
507
 * check for other values for the same key in the list.
508
 *
509
 * @param pszKey the key name to add.
510
 * @param pszValue the key value to add.
511
 */
512
513
CPLStringList &CPLStringList::AddNameValue(const char *pszKey,
514
                                           const char *pszValue)
515
516
60.0M
{
517
60.0M
    if (pszKey == nullptr || pszValue == nullptr)
518
77.6k
        return *this;
519
520
60.0M
    if (!MakeOurOwnCopy())
521
0
        return *this;
522
523
    /* -------------------------------------------------------------------- */
524
    /*      Format the line.                                                */
525
    /* -------------------------------------------------------------------- */
526
60.0M
    if (strlen(pszKey) >
527
60.0M
            std::numeric_limits<size_t>::max() - strlen(pszValue) ||
528
60.0M
        strlen(pszKey) + strlen(pszValue) >
529
60.0M
            std::numeric_limits<size_t>::max() - 2)
530
0
    {
531
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
532
0
                 "Too big strings in AddNameValue()");
533
0
        return *this;
534
0
    }
535
60.0M
    const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
536
60.0M
    char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
537
60.0M
    if (pszLine == nullptr)
538
0
        return *this;
539
60.0M
    snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
540
541
    /* -------------------------------------------------------------------- */
542
    /*      If we don't need to keep the sort order things are pretty       */
543
    /*      straight forward.                                               */
544
    /* -------------------------------------------------------------------- */
545
60.0M
    if (!IsSorted())
546
59.7M
        return AddStringDirectly(pszLine);
547
548
    /* -------------------------------------------------------------------- */
549
    /*      Find the proper insertion point.                                */
550
    /* -------------------------------------------------------------------- */
551
278k
    CPLAssert(IsSorted());
552
278k
    const int iKey = FindSortedInsertionPoint(pszLine);
553
278k
    InsertStringDirectly(iKey, pszLine);
554
278k
    bIsSorted = true;  // We have actually preserved sort order.
555
556
278k
    return *this;
557
60.0M
}
558
559
/************************************************************************/
560
/*                            SetNameValue()                            */
561
/************************************************************************/
562
563
/**
564
 * Set name=value entry in the list.
565
 *
566
 * Similar to AddNameValue(), except if there is already a value for
567
 * the key in the list it is replaced instead of adding a new entry to
568
 * the list.  If pszValue is NULL any existing key entry is removed.
569
 *
570
 * @param pszKey the key name to add.
571
 * @param pszValue the key value to add.
572
 */
573
574
CPLStringList &CPLStringList::SetNameValue(const char *pszKey,
575
                                           const char *pszValue)
576
577
59.4M
{
578
59.4M
    int iKey = FindName(pszKey);
579
580
59.4M
    if (iKey == -1)
581
58.4M
        return AddNameValue(pszKey, pszValue);
582
583
923k
    Count();
584
923k
    if (!MakeOurOwnCopy())
585
0
        return *this;
586
587
923k
    CPLFree(papszList[iKey]);
588
923k
    if (pszValue == nullptr)  // delete entry
589
0
    {
590
591
        // shift everything down by one.
592
0
        do
593
0
        {
594
0
            papszList[iKey] = papszList[iKey + 1];
595
0
        } while (papszList[iKey++] != nullptr);
596
597
0
        nCount--;
598
0
    }
599
923k
    else
600
923k
    {
601
923k
        if (strlen(pszKey) >
602
923k
                std::numeric_limits<size_t>::max() - strlen(pszValue) ||
603
923k
            strlen(pszKey) + strlen(pszValue) >
604
923k
                std::numeric_limits<size_t>::max() - 2)
605
0
        {
606
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
607
0
                     "Too big strings in AddNameValue()");
608
0
            return *this;
609
0
        }
610
923k
        const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
611
923k
        char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
612
923k
        if (pszLine == nullptr)
613
0
            return *this;
614
923k
        snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
615
616
923k
        papszList[iKey] = pszLine;
617
923k
    }
618
619
923k
    return *this;
620
923k
}
621
622
/************************************************************************/
623
/*                             SetString()                              */
624
/************************************************************************/
625
626
/**
627
 * Replace a string within the list.
628
 *
629
 * @param pos 0-index position of the string to replace
630
 * @param pszString value to be used (will be copied)
631
 * @return a reference to the CPLStringList on which it was invoked.
632
 * @since 3.13
633
 */
634
CPLStringList &CPLStringList::SetString(int pos, const char *pszString)
635
0
{
636
0
    return SetStringDirectly(pos, VSI_STRDUP_VERBOSE(pszString));
637
0
}
638
639
/**
640
 * Replace a string within the list.
641
 *
642
 * @param pos 0-index position of the string to replace
643
 * @param osString value to be used (will be copied)
644
 * @return a reference to the CPLStringList on which it was invoked.
645
 * @since 3.13
646
 */
647
CPLStringList &CPLStringList::SetString(int pos, const std::string &osString)
648
0
{
649
0
    return SetString(pos, osString.c_str());
650
0
}
651
652
/**
653
 * Replace a string within the list.
654
 *
655
 * @param pos 0-index position of the string to replace
656
 * @param pszString value to be used (ownership is taken)
657
 * @return a reference to the CPLStringList on which it was invoked.
658
 * @since 3.13
659
 */
660
CPLStringList &CPLStringList::SetStringDirectly(int pos, char *pszString)
661
0
{
662
0
    if (!MakeOurOwnCopy())
663
0
        return *this;
664
665
0
    CPLFree(papszList[pos]);
666
0
    papszList[pos] = pszString;
667
668
0
    if (bIsSorted)
669
0
    {
670
0
        if (pos > 0 &&
671
0
            CPLCompareKeyValueString(papszList[pos], papszList[pos - 1]) == -1)
672
0
        {
673
0
            bIsSorted = false;
674
0
        }
675
0
        if (pos < Count() - 1 &&
676
0
            CPLCompareKeyValueString(papszList[pos], papszList[pos + 1]) == 1)
677
0
        {
678
0
            bIsSorted = false;
679
0
        }
680
0
    }
681
682
0
    return *this;
683
0
}
684
685
/************************************************************************/
686
/*                              operator[]                              */
687
/************************************************************************/
688
689
/**
690
 * Fetch entry "i".
691
 *
692
 * Fetches the requested item in the list.  Note that the returned string
693
 * remains owned by the CPLStringList.  If "i" is out of range NULL is
694
 * returned.
695
 *
696
 * @param i the index of the list item to return.
697
 * @return selected entry in the list.
698
 */
699
char *CPLStringList::operator[](int i)
700
701
27.3M
{
702
27.3M
    if (nCount == -1)
703
0
        Count();
704
705
27.3M
    if (i < 0 || i >= nCount)
706
976
        return nullptr;
707
708
27.3M
    return papszList[i];
709
27.3M
}
710
711
const char *CPLStringList::operator[](int i) const
712
713
22.5M
{
714
22.5M
    if (nCount == -1)
715
6.65k
        Count();
716
717
22.5M
    if (i < 0 || i >= nCount)
718
0
        return nullptr;
719
720
22.5M
    return papszList[i];
721
22.5M
}
722
723
/************************************************************************/
724
/*                             StealList()                              */
725
/************************************************************************/
726
727
/**
728
 * Seize ownership of underlying string array.
729
 *
730
 * This method is similar to List(), except that the returned list is
731
 * now owned by the caller and the CPLStringList is emptied.
732
 *
733
 * @return the C style string list.
734
 */
735
char **CPLStringList::StealList()
736
737
101M
{
738
101M
    char **papszRetList = papszList;
739
740
101M
    bOwnList = false;
741
101M
    papszList = nullptr;
742
101M
    nCount = 0;
743
101M
    nAllocation = 0;
744
745
101M
    return papszRetList;
746
101M
}
747
748
/* Case insensitive comparison function */
749
static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb)
750
9.03M
{
751
9.03M
    const char *pszItera = pszKVa;
752
9.03M
    const char *pszIterb = pszKVb;
753
109M
    while (true)
754
109M
    {
755
109M
        char cha = *pszItera;
756
109M
        char chb = *pszIterb;
757
109M
        if (cha == '=' || cha == '\0')
758
872k
        {
759
872k
            if (chb == '=' || chb == '\0')
760
771k
                return 0;
761
101k
            else
762
101k
                return -1;
763
872k
        }
764
108M
        if (chb == '=' || chb == '\0')
765
73.2k
        {
766
73.2k
            return 1;
767
73.2k
        }
768
108M
        if (cha >= 'a' && cha <= 'z')
769
12.2M
            cha -= ('a' - 'A');
770
108M
        if (chb >= 'a' && chb <= 'z')
771
12.1M
            chb -= ('a' - 'A');
772
108M
        if (cha < chb)
773
3.97M
            return -1;
774
104M
        else if (cha > chb)
775
4.10M
            return 1;
776
99.9M
        pszItera++;
777
99.9M
        pszIterb++;
778
99.9M
    }
779
9.03M
}
780
781
/************************************************************************/
782
/*                                Sort()                                */
783
/************************************************************************/
784
785
/**
786
 * Sort the entries in the list and mark list sorted.
787
 *
788
 * Note that once put into "sorted" mode, the CPLStringList will attempt to
789
 * keep things in sorted order through calls to AddString(),
790
 * AddStringDirectly(), AddNameValue(), SetNameValue(). Complete list
791
 * assignments (via Assign() and operator= will clear the sorting state.
792
 * When in sorted order FindName(), FetchNameValue() and FetchNameValueDef()
793
 * will do a binary search to find the key, substantially improve lookup
794
 * performance in large lists.
795
 */
796
797
CPLStringList &CPLStringList::Sort()
798
799
483k
{
800
483k
    Count();
801
483k
    if (!MakeOurOwnCopy())
802
0
        return *this;
803
804
483k
    if (nCount > 1)
805
55.7k
    {
806
55.7k
        std::sort(papszList, papszList + nCount,
807
55.7k
                  [](const char *a, const char *b)
808
7.17M
                  { return CPLCompareKeyValueString(a, b) < 0; });
809
55.7k
    }
810
483k
    bIsSorted = true;
811
812
483k
    return *this;
813
483k
}
814
815
/************************************************************************/
816
/*                              FindName()                              */
817
/************************************************************************/
818
819
/**
820
 * Get index of given name/value keyword.
821
 *
822
 * Note that this search is for a line in the form name=value or name:value.
823
 * Use FindString() or PartialFindString() for searches not based on name=value
824
 * pairs.
825
 *
826
 * @param pszKey the name to search for.
827
 *
828
 * @return the string list index of this name, or -1 on failure.
829
 */
830
831
int CPLStringList::FindName(const char *pszKey) const
832
833
322M
{
834
322M
    if (!IsSorted())
835
321M
        return CSLFindName(papszList, pszKey);
836
837
    // If we are sorted, we can do an optimized binary search.
838
628k
    int iStart = 0;
839
628k
    int iEnd = nCount - 1;
840
628k
    size_t nKeyLen = strlen(pszKey);
841
842
1.92M
    while (iStart <= iEnd)
843
1.46M
    {
844
1.46M
        const int iMiddle = (iEnd + iStart) / 2;
845
1.46M
        const char *pszMiddle = papszList[iMiddle];
846
847
1.46M
        if (EQUALN(pszMiddle, pszKey, nKeyLen) &&
848
177k
            (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':'))
849
169k
            return iMiddle;
850
851
1.29M
        if (CPLCompareKeyValueString(pszKey, pszMiddle) < 0)
852
235k
            iEnd = iMiddle - 1;
853
1.05M
        else
854
1.05M
            iStart = iMiddle + 1;
855
1.29M
    }
856
857
458k
    return -1;
858
628k
}
859
860
/************************************************************************/
861
/*                             FetchBool()                              */
862
/************************************************************************/
863
/**
864
 *
865
 * Check for boolean key value.
866
 *
867
 * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
868
 * with the given name, and if it can be interpreted as being TRUE.  If
869
 * the key appears without any "=Value" portion it will be considered true.
870
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
871
 * if the key appears in the list it will be considered TRUE.  If the key
872
 * doesn't appear at all, the indicated default value will be returned.
873
 *
874
 * @param pszKey the key value to look for (case insensitive).
875
 * @param bDefault the value to return if the key isn't found at all.
876
 *
877
 * @return true or false
878
 */
879
880
bool CPLStringList::FetchBool(const char *pszKey, bool bDefault) const
881
882
16.2M
{
883
16.2M
    const char *pszValue = FetchNameValue(pszKey);
884
885
16.2M
    if (pszValue == nullptr)
886
16.2M
        return bDefault;
887
888
199
    return CPLTestBool(pszValue);
889
16.2M
}
890
891
/************************************************************************/
892
/*                            FetchBoolean()                            */
893
/************************************************************************/
894
/**
895
 *
896
 * DEPRECATED: Check for boolean key value.
897
 *
898
 * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
899
 * with the given name, and if it can be interpreted as being TRUE.  If
900
 * the key appears without any "=Value" portion it will be considered true.
901
 * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
902
 * if the key appears in the list it will be considered TRUE.  If the key
903
 * doesn't appear at all, the indicated default value will be returned.
904
 *
905
 * @param pszKey the key value to look for (case insensitive).
906
 * @param bDefault the value to return if the key isn't found at all.
907
 *
908
 * @return TRUE or FALSE
909
 */
910
911
int CPLStringList::FetchBoolean(const char *pszKey, int bDefault) const
912
913
16.2M
{
914
16.2M
    return FetchBool(pszKey, CPL_TO_BOOL(bDefault)) ? TRUE : FALSE;
915
16.2M
}
916
917
/************************************************************************/
918
/*                           FetchNameValue()                           */
919
/************************************************************************/
920
921
/**
922
 * Fetch value associated with this key name.
923
 *
924
 * If this list sorted, a fast binary search is done, otherwise a linear
925
 * scan is done.  Name lookup is case insensitive.
926
 *
927
 * @param pszName the key name to search for.
928
 *
929
 * @return the corresponding value or NULL if not found.  The returned string
930
 * should not be modified and points into internal object state that may
931
 * change on future calls.
932
 */
933
934
const char *CPLStringList::FetchNameValue(const char *pszName) const
935
936
263M
{
937
263M
    const int iKey = FindName(pszName);
938
939
263M
    if (iKey == -1)
940
78.6M
        return nullptr;
941
942
184M
    CPLAssert(papszList[iKey][strlen(pszName)] == '=' ||
943
184M
              papszList[iKey][strlen(pszName)] == ':');
944
945
184M
    return papszList[iKey] + strlen(pszName) + 1;
946
263M
}
947
948
/************************************************************************/
949
/*                         FetchNameValueDef()                          */
950
/************************************************************************/
951
952
/**
953
 * Fetch value associated with this key name.
954
 *
955
 * If this list sorted, a fast binary search is done, otherwise a linear
956
 * scan is done.  Name lookup is case insensitive.
957
 *
958
 * @param pszName the key name to search for.
959
 * @param pszDefault the default value returned if the named entry isn't found.
960
 *
961
 * @return the corresponding value or the passed default if not found.
962
 */
963
964
const char *CPLStringList::FetchNameValueDef(const char *pszName,
965
                                             const char *pszDefault) const
966
967
6.34M
{
968
6.34M
    const char *pszValue = FetchNameValue(pszName);
969
6.34M
    if (pszValue == nullptr)
970
6.18M
        return pszDefault;
971
972
162k
    return pszValue;
973
6.34M
}
974
975
/************************************************************************/
976
/*                            InsertString()                            */
977
/************************************************************************/
978
979
/**
980
 * \fn CPLStringList *CPLStringList::InsertString( int nInsertAtLineNo,
981
 *                                                 const char *pszNewLine );
982
 *
983
 * \brief Insert into the list at identified location.
984
 *
985
 * This method will insert a string into the list at the identified
986
 * location.  The insertion point must be within or at the end of the list.
987
 * The following entries are pushed down to make space.
988
 *
989
 * @param nInsertAtLineNo the line to insert at, zero to insert at front.
990
 * @param pszNewLine to the line to insert.  This string will be copied.
991
 */
992
993
/************************************************************************/
994
/*                        InsertStringDirectly()                        */
995
/************************************************************************/
996
997
/**
998
 * Insert into the list at identified location.
999
 *
1000
 * This method will insert a string into the list at the identified
1001
 * location.  The insertion point must be within or at the end of the list.
1002
 * The following entries are pushed down to make space.
1003
 *
1004
 * @param nInsertAtLineNo the line to insert at, zero to insert at front.
1005
 * @param pszNewLine to the line to insert, the ownership of this string
1006
 * will be taken over the by the object.  It must have been allocated on the
1007
 * heap.
1008
 */
1009
1010
CPLStringList &CPLStringList::InsertStringDirectly(int nInsertAtLineNo,
1011
                                                   char *pszNewLine)
1012
1013
278k
{
1014
278k
    if (nCount == -1)
1015
0
        Count();
1016
1017
278k
    if (!EnsureAllocation(nCount + 1))
1018
0
    {
1019
0
        VSIFree(pszNewLine);
1020
0
        return *this;
1021
0
    }
1022
1023
278k
    if (nInsertAtLineNo < 0 || nInsertAtLineNo > nCount)
1024
0
    {
1025
0
        CPLError(CE_Failure, CPLE_AppDefined,
1026
0
                 "CPLStringList::InsertString() requested beyond list end.");
1027
0
        return *this;
1028
0
    }
1029
1030
278k
    bIsSorted = false;
1031
1032
734k
    for (int i = nCount; i > nInsertAtLineNo; i--)
1033
455k
        papszList[i] = papszList[i - 1];
1034
1035
278k
    papszList[nInsertAtLineNo] = pszNewLine;
1036
278k
    papszList[++nCount] = nullptr;
1037
1038
278k
    return *this;
1039
278k
}
1040
1041
/************************************************************************/
1042
/*                           RemoveStrings()                            */
1043
/************************************************************************/
1044
1045
/**
1046
 * Remove strings inside a CPLStringList.
1047
 *
1048
 * @param nFirstLineToDelete the 0-based index of the first string to
1049
 * remove. If this value is -1 or is larger than the actual
1050
 * number of strings in list then the nNumToRemove last strings are
1051
 * removed.
1052
 * @param nNumToRemove the number of strings to remove
1053
 *
1054
 * @return a reference to the CPLStringList on which it was invoked.
1055
 * @since 3.13
1056
 */
1057
CPLStringList &CPLStringList::RemoveStrings(int nFirstLineToDelete,
1058
                                            int nNumToRemove)
1059
0
{
1060
0
    if (!MakeOurOwnCopy())
1061
0
        return *this;
1062
1063
0
    papszList =
1064
0
        CSLRemoveStrings(papszList, nFirstLineToDelete, nNumToRemove, nullptr);
1065
0
    nCount = -1;
1066
0
    return *this;
1067
0
}
1068
1069
/************************************************************************/
1070
/*                      FindSortedInsertionPoint()                      */
1071
/*                                                                      */
1072
/*      Find the location at which the indicated line should be         */
1073
/*      inserted in order to keep things in sorted order.               */
1074
/************************************************************************/
1075
1076
int CPLStringList::FindSortedInsertionPoint(const char *pszLine)
1077
1078
278k
{
1079
278k
    CPLAssert(IsSorted());
1080
1081
278k
    int iStart = 0;
1082
278k
    int iEnd = nCount - 1;
1083
1084
839k
    while (iStart <= iEnd)
1085
561k
    {
1086
561k
        const int iMiddle = (iEnd + iStart) / 2;
1087
561k
        const char *pszMiddle = papszList[iMiddle];
1088
1089
561k
        if (CPLCompareKeyValueString(pszLine, pszMiddle) < 0)
1090
75.2k
            iEnd = iMiddle - 1;
1091
486k
        else
1092
486k
            iStart = iMiddle + 1;
1093
561k
    }
1094
1095
278k
    iEnd++;
1096
278k
    CPLAssert(iEnd >= 0 && iEnd <= nCount);
1097
278k
    CPLAssert(iEnd == 0 ||
1098
278k
              CPLCompareKeyValueString(pszLine, papszList[iEnd - 1]) >= 0);
1099
278k
    CPLAssert(iEnd == nCount ||
1100
278k
              CPLCompareKeyValueString(pszLine, papszList[iEnd]) <= 0);
1101
1102
278k
    return iEnd;
1103
278k
}
1104
1105
namespace cpl
1106
{
1107
1108
/************************************************************************/
1109
/*          CSLIterator::operator==(const CSLIterator &other)           */
1110
/************************************************************************/
1111
1112
/*! @cond Doxygen_Suppress */
1113
bool CSLIterator::operator==(const CSLIterator &other) const
1114
57.3M
{
1115
57.3M
    if (!m_bAtEnd && other.m_bAtEnd)
1116
57.3M
    {
1117
57.3M
        return m_papszList == nullptr || *m_papszList == nullptr;
1118
57.3M
    }
1119
0
    if (!m_bAtEnd && !other.m_bAtEnd)
1120
0
    {
1121
0
        return m_papszList == other.m_papszList;
1122
0
    }
1123
0
    if (m_bAtEnd && other.m_bAtEnd)
1124
0
    {
1125
0
        return true;
1126
0
    }
1127
0
    return false;
1128
0
}
1129
1130
/*! @endcond */
1131
1132
/************************************************************************/
1133
/*                  CSLNameValueIterator::operator*()                   */
1134
/************************************************************************/
1135
1136
/*! @cond Doxygen_Suppress */
1137
CSLNameValueIterator::value_type CSLNameValueIterator::operator*()
1138
34.7k
{
1139
34.7k
    if (m_papszList)
1140
34.7k
    {
1141
46.8k
        while (*m_papszList)
1142
46.8k
        {
1143
46.8k
            char *pszKey = nullptr;
1144
46.8k
            const char *pszValue = CPLParseNameValue(*m_papszList, &pszKey);
1145
46.8k
            if (pszKey)
1146
34.6k
            {
1147
34.6k
                m_osKey = pszKey;
1148
34.6k
                CPLFree(pszKey);
1149
34.6k
                return {m_osKey.c_str(), pszValue};
1150
34.6k
            }
1151
12.1k
            else if (m_bReturnNullKeyIfNotNameValue)
1152
8
            {
1153
8
                return {nullptr, *m_papszList};
1154
8
            }
1155
            // Skip entries that are not name=value pairs.
1156
12.1k
            ++m_papszList;
1157
12.1k
        }
1158
34.7k
    }
1159
    // Should not happen
1160
0
    CPLAssert(false);
1161
0
    return {"", ""};
1162
34.7k
}
1163
1164
/*! @endcond */
1165
1166
/************************************************************************/
1167
/*                  CSLNameValueIteratorWrapper::end()                  */
1168
/************************************************************************/
1169
1170
/*! @cond Doxygen_Suppress */
1171
CSLNameValueIterator CSLNameValueIteratorWrapper::end() const
1172
12.0k
{
1173
12.0k
    int nCount = CSLCount(m_papszList);
1174
12.0k
    if (!m_bReturnNullKeyIfNotNameValue)
1175
12.0k
    {
1176
19.2k
        while (nCount > 0 && strchr(m_papszList[nCount - 1], '=') == nullptr)
1177
7.12k
            --nCount;
1178
12.0k
    }
1179
12.0k
    return CSLNameValueIterator{m_papszList + nCount,
1180
12.0k
                                m_bReturnNullKeyIfNotNameValue};
1181
12.0k
}
1182
1183
/*! @endcond */
1184
1185
}  // namespace cpl