Line data Source code
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 : 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 2030440 : CPLStringList::CPLStringList(char **papszListIn, int bTakeOwnership)
50 2030440 : : CPLStringList()
51 :
52 : {
53 2028250 : Assign(papszListIn, bTakeOwnership);
54 2021000 : }
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 14565 : CPLStringList::CPLStringList(CSLConstList papszListIn) : CPLStringList()
69 :
70 : {
71 14560 : Assign(CSLDuplicate(papszListIn));
72 14569 : }
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 4076 : CPLStringList::CPLStringList(const std::vector<std::string> &aosList)
88 : {
89 4076 : if (!aosList.empty())
90 : {
91 620 : bOwnList = true;
92 620 : papszList = static_cast<char **>(
93 620 : VSI_CALLOC_VERBOSE(aosList.size() + 1, sizeof(char *)));
94 620 : nCount = static_cast<int>(aosList.size());
95 3901 : for (int i = 0; i < nCount; ++i)
96 : {
97 3281 : papszList[i] = VSI_STRDUP_VERBOSE(aosList[i].c_str());
98 : }
99 : }
100 4076 : }
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 3 : CPLStringList::CPLStringList(std::initializer_list<const char *> oInitList)
116 : {
117 9 : for (const char *pszStr : oInitList)
118 : {
119 6 : AddString(pszStr);
120 : }
121 3 : }
122 :
123 : /************************************************************************/
124 : /* CPLStringList() */
125 : /************************************************************************/
126 :
127 : //! Copy constructor
128 33167 : CPLStringList::CPLStringList(const CPLStringList &oOther) : CPLStringList()
129 :
130 : {
131 33167 : operator=(oOther);
132 33166 : }
133 :
134 : /************************************************************************/
135 : /* CPLStringList() */
136 : /************************************************************************/
137 :
138 : //! Move constructor
139 3634080 : CPLStringList::CPLStringList(CPLStringList &&oOther) : CPLStringList()
140 :
141 : {
142 3630580 : operator=(std::move(oOther));
143 3626550 : }
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 94 : const CPLStringList CPLStringList::BoundToConstList(CSLConstList papszListIn)
161 : {
162 : return CPLStringList(const_cast<char **>(papszListIn),
163 94 : /* bTakeOwnership= */ false);
164 : }
165 :
166 : /************************************************************************/
167 : /* operator=() */
168 : /************************************************************************/
169 :
170 287038 : CPLStringList &CPLStringList::operator=(const CPLStringList &oOther)
171 : {
172 287038 : if (this != &oOther)
173 : {
174 287037 : char **l_papszList = CSLDuplicate(oOther.papszList);
175 287035 : if (l_papszList)
176 : {
177 12187 : Assign(l_papszList, TRUE);
178 12188 : nAllocation = oOther.nCount > 0 ? oOther.nCount + 1 : 0;
179 12188 : nCount = oOther.nCount;
180 12188 : bIsSorted = oOther.bIsSorted;
181 : }
182 : }
183 :
184 287037 : return *this;
185 : }
186 :
187 : /************************************************************************/
188 : /* operator=() */
189 : /************************************************************************/
190 :
191 3633040 : CPLStringList &CPLStringList::operator=(CPLStringList &&oOther)
192 : {
193 3633040 : if (this != &oOther)
194 : {
195 3634620 : Clear();
196 3635200 : papszList = oOther.papszList;
197 3635200 : oOther.papszList = nullptr;
198 3635200 : nCount = oOther.nCount;
199 3635200 : oOther.nCount = 0;
200 3635200 : nAllocation = oOther.nAllocation;
201 3635200 : oOther.nAllocation = 0;
202 3635200 : bOwnList = oOther.bOwnList;
203 3635200 : oOther.bOwnList = false;
204 3635200 : bIsSorted = oOther.bIsSorted;
205 3635200 : oOther.bIsSorted = true;
206 : }
207 :
208 3633630 : return *this;
209 : }
210 :
211 : /************************************************************************/
212 : /* operator=() */
213 : /************************************************************************/
214 :
215 102792 : CPLStringList &CPLStringList::operator=(CSLConstList papszListIn)
216 : {
217 102792 : if (papszListIn != papszList)
218 : {
219 16238 : Assign(CSLDuplicate(papszListIn));
220 16238 : bIsSorted = false;
221 : }
222 :
223 102792 : return *this;
224 : }
225 :
226 : /************************************************************************/
227 : /* ~CPLStringList() */
228 : /************************************************************************/
229 :
230 25274300 : CPLStringList::~CPLStringList()
231 :
232 : {
233 12643000 : Clear();
234 12631300 : }
235 :
236 : /************************************************************************/
237 : /* Clear() */
238 : /************************************************************************/
239 :
240 : /**
241 : * Clear the string list.
242 : */
243 18853700 : CPLStringList &CPLStringList::Clear()
244 :
245 : {
246 18853700 : if (bOwnList)
247 : {
248 3426540 : CSLDestroy(papszList);
249 3423700 : papszList = nullptr;
250 :
251 3423700 : bOwnList = FALSE;
252 3423700 : nAllocation = 0;
253 3423700 : nCount = 0;
254 : }
255 :
256 18850800 : return *this;
257 : }
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 2464040 : CPLStringList &CPLStringList::Assign(char **papszListIn, int bTakeOwnership)
275 :
276 : {
277 2464040 : Clear();
278 :
279 2462950 : papszList = papszListIn;
280 2462950 : bOwnList = CPL_TO_BOOL(bTakeOwnership);
281 :
282 2457180 : if (papszList == nullptr || *papszList == nullptr)
283 1880370 : nCount = 0;
284 : else
285 576806 : nCount = -1; // unknown
286 :
287 2457180 : nAllocation = 0;
288 2457180 : bIsSorted = FALSE;
289 :
290 2457180 : return *this;
291 : }
292 :
293 : /************************************************************************/
294 : /* Count() */
295 : /************************************************************************/
296 :
297 : /**
298 : * @return count of strings in the list, zero if empty.
299 : */
300 :
301 3512760 : int CPLStringList::Count() const
302 :
303 : {
304 3512760 : if (nCount == -1)
305 : {
306 546529 : if (papszList == nullptr)
307 : {
308 0 : nCount = 0;
309 0 : nAllocation = 0;
310 : }
311 : else
312 : {
313 546529 : nCount = CSLCount(papszList);
314 546529 : nAllocation = std::max(nCount + 1, nAllocation);
315 : }
316 : }
317 :
318 3512760 : return nCount;
319 : }
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 8270240 : bool CPLStringList::MakeOurOwnCopy()
329 :
330 : {
331 8270240 : if (bOwnList)
332 4645090 : return true;
333 :
334 3625160 : if (papszList == nullptr)
335 3625050 : return true;
336 :
337 103 : Count();
338 90 : char **papszListNew = CSLDuplicate(papszList);
339 90 : if (papszListNew == nullptr)
340 : {
341 0 : return false;
342 : }
343 90 : papszList = papszListNew;
344 90 : bOwnList = true;
345 90 : nAllocation = nCount + 1;
346 90 : return true;
347 : }
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 11647300 : bool CPLStringList::EnsureAllocation(int nMaxList)
358 :
359 : {
360 11647300 : if (!bOwnList)
361 : {
362 2861020 : if (!MakeOurOwnCopy())
363 0 : return false;
364 : }
365 :
366 11647300 : if (papszList == nullptr || nAllocation <= nMaxList)
367 : {
368 : // we need to be able to store nMaxList+1 as an int,
369 : // and allocate (nMaxList+1) * sizeof(char*) bytes
370 5907710 : if (nMaxList < 0 || nMaxList > std::numeric_limits<int>::max() - 1 ||
371 2953880 : static_cast<size_t>(nMaxList) >
372 2953880 : std::numeric_limits<size_t>::max() / sizeof(char *) - 1)
373 : {
374 0 : return false;
375 : }
376 2953840 : int nNewAllocation = nMaxList + 1;
377 2953840 : if (nNewAllocation <= (std::numeric_limits<int>::max() - 20) / 2 /
378 : static_cast<int>(sizeof(char *)))
379 2953840 : nNewAllocation = std::max(nNewAllocation * 2 + 20, nMaxList + 1);
380 2953830 : if (papszList == nullptr)
381 : {
382 2863850 : papszList = static_cast<char **>(
383 2863830 : VSI_CALLOC_VERBOSE(nNewAllocation, sizeof(char *)));
384 2863850 : bOwnList = true;
385 2863850 : nCount = 0;
386 2863850 : if (papszList == nullptr)
387 0 : return false;
388 : }
389 : else
390 : {
391 90003 : char **papszListNew = static_cast<char **>(VSI_REALLOC_VERBOSE(
392 : papszList, nNewAllocation * sizeof(char *)));
393 90003 : if (papszListNew == nullptr)
394 0 : return false;
395 90003 : papszList = papszListNew;
396 : }
397 2953850 : nAllocation = nNewAllocation;
398 : }
399 11647200 : return true;
400 : }
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 11640300 : CPLStringList &CPLStringList::AddStringDirectly(char *pszNewString)
416 :
417 : {
418 11640300 : if (nCount == -1)
419 560 : Count();
420 :
421 11640300 : if (!EnsureAllocation(nCount + 1))
422 : {
423 21 : VSIFree(pszNewString);
424 0 : return *this;
425 : }
426 :
427 11640200 : papszList[nCount++] = pszNewString;
428 11640200 : papszList[nCount] = nullptr;
429 :
430 11640200 : bIsSorted = false;
431 :
432 11640200 : return *this;
433 : }
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 6408990 : CPLStringList &CPLStringList::AddString(const char *pszNewString)
448 :
449 : {
450 6408990 : char *pszDupString = VSI_STRDUP_VERBOSE(pszNewString);
451 6409000 : if (pszDupString == nullptr)
452 0 : return *this;
453 6409000 : return AddStringDirectly(pszDupString);
454 : }
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 26177 : CPLStringList &CPLStringList::AddString(const std::string &newString)
469 : {
470 26177 : return AddString(newString.c_str());
471 : }
472 :
473 : /************************************************************************/
474 : /* AddNameValue() */
475 : /************************************************************************/
476 :
477 : /**
478 : * Add a name=value entry to the list.
479 : *
480 : * A key=value string is prepared and appended to the list. There is no
481 : * check for other values for the same key in the list.
482 : *
483 : * @param pszKey the key name to add.
484 : * @param pszValue the key value to add.
485 : */
486 :
487 5340560 : CPLStringList &CPLStringList::AddNameValue(const char *pszKey,
488 : const char *pszValue)
489 :
490 : {
491 5340560 : if (pszKey == nullptr || pszValue == nullptr)
492 143983 : return *this;
493 :
494 5196580 : if (!MakeOurOwnCopy())
495 0 : return *this;
496 :
497 : /* -------------------------------------------------------------------- */
498 : /* Format the line. */
499 : /* -------------------------------------------------------------------- */
500 10393200 : if (strlen(pszKey) >
501 10393200 : std::numeric_limits<size_t>::max() - strlen(pszValue) ||
502 5196580 : strlen(pszKey) + strlen(pszValue) >
503 5196580 : std::numeric_limits<size_t>::max() - 2)
504 : {
505 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
506 : "Too big strings in AddNameValue()");
507 0 : return *this;
508 : }
509 5196600 : const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
510 5196600 : char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
511 5196590 : if (pszLine == nullptr)
512 0 : return *this;
513 5196590 : snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* If we don't need to keep the sort order things are pretty */
517 : /* straight forward. */
518 : /* -------------------------------------------------------------------- */
519 5196590 : if (!IsSorted())
520 5189960 : return AddStringDirectly(pszLine);
521 :
522 : /* -------------------------------------------------------------------- */
523 : /* Find the proper insertion point. */
524 : /* -------------------------------------------------------------------- */
525 6635 : CPLAssert(IsSorted());
526 6625 : const int iKey = FindSortedInsertionPoint(pszLine);
527 6625 : InsertStringDirectly(iKey, pszLine);
528 6625 : bIsSorted = true; // We have actually preserved sort order.
529 :
530 6625 : return *this;
531 : }
532 :
533 : /************************************************************************/
534 : /* SetNameValue() */
535 : /************************************************************************/
536 :
537 : /**
538 : * Set name=value entry in the list.
539 : *
540 : * Similar to AddNameValue(), except if there is already a value for
541 : * the key in the list it is replaced instead of adding a new entry to
542 : * the list. If pszValue is NULL any existing key entry is removed.
543 : *
544 : * @param pszKey the key name to add.
545 : * @param pszValue the key value to add.
546 : */
547 :
548 5418280 : CPLStringList &CPLStringList::SetNameValue(const char *pszKey,
549 : const char *pszValue)
550 :
551 : {
552 5418280 : int iKey = FindName(pszKey);
553 :
554 5418260 : if (iKey == -1)
555 5304600 : return AddNameValue(pszKey, pszValue);
556 :
557 113655 : Count();
558 113656 : if (!MakeOurOwnCopy())
559 0 : return *this;
560 :
561 113653 : CPLFree(papszList[iKey]);
562 113656 : if (pszValue == nullptr) // delete entry
563 : {
564 :
565 : // shift everything down by one.
566 558 : do
567 : {
568 2900 : papszList[iKey] = papszList[iKey + 1];
569 2900 : } while (papszList[iKey++] != nullptr);
570 :
571 2342 : nCount--;
572 : }
573 : else
574 : {
575 222627 : if (strlen(pszKey) >
576 222627 : std::numeric_limits<size_t>::max() - strlen(pszValue) ||
577 111311 : strlen(pszKey) + strlen(pszValue) >
578 111311 : std::numeric_limits<size_t>::max() - 2)
579 : {
580 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
581 : "Too big strings in AddNameValue()");
582 0 : return *this;
583 : }
584 111313 : const size_t nLen = strlen(pszKey) + strlen(pszValue) + 2;
585 111313 : char *pszLine = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
586 111311 : if (pszLine == nullptr)
587 0 : return *this;
588 111311 : snprintf(pszLine, nLen, "%s=%s", pszKey, pszValue);
589 :
590 111311 : papszList[iKey] = pszLine;
591 : }
592 :
593 113653 : return *this;
594 : }
595 :
596 : /************************************************************************/
597 : /* SetString() */
598 : /************************************************************************/
599 :
600 : /**
601 : * Replace a string within the list.
602 : *
603 : * @param pos 0-index position of the string to replace
604 : * @param pszString value to be used (will be copied)
605 : * @return a reference to the CPLStringList on which it was invoked.
606 : * @since 3.13
607 : */
608 6 : CPLStringList &CPLStringList::SetString(int pos, const char *pszString)
609 : {
610 6 : return SetStringDirectly(pos, VSI_STRDUP_VERBOSE(pszString));
611 : }
612 :
613 : /**
614 : * Replace a string within the list.
615 : *
616 : * @param pos 0-index position of the string to replace
617 : * @param osString value to be used (will be copied)
618 : * @return a reference to the CPLStringList on which it was invoked.
619 : * @since 3.13
620 : */
621 1 : CPLStringList &CPLStringList::SetString(int pos, const std::string &osString)
622 : {
623 1 : return SetString(pos, osString.c_str());
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 (ownership is taken)
631 : * @return a reference to the CPLStringList on which it was invoked.
632 : * @since 3.13
633 : */
634 6 : CPLStringList &CPLStringList::SetStringDirectly(int pos, char *pszString)
635 : {
636 6 : if (!MakeOurOwnCopy())
637 0 : return *this;
638 :
639 6 : CPLFree(papszList[pos]);
640 6 : papszList[pos] = pszString;
641 :
642 6 : if (bIsSorted)
643 : {
644 8 : if (pos > 0 &&
645 2 : CPLCompareKeyValueString(papszList[pos], papszList[pos - 1]) == -1)
646 : {
647 0 : bIsSorted = false;
648 : }
649 11 : if (pos < Count() - 1 &&
650 5 : CPLCompareKeyValueString(papszList[pos], papszList[pos + 1]) == 1)
651 : {
652 3 : bIsSorted = false;
653 : }
654 : }
655 :
656 6 : return *this;
657 : }
658 :
659 : /************************************************************************/
660 : /* operator[] */
661 : /************************************************************************/
662 :
663 : /**
664 : * Fetch entry "i".
665 : *
666 : * Fetches the requested item in the list. Note that the returned string
667 : * remains owned by the CPLStringList. If "i" is out of range NULL is
668 : * returned.
669 : *
670 : * @param i the index of the list item to return.
671 : * @return selected entry in the list.
672 : */
673 1441970 : char *CPLStringList::operator[](int i)
674 :
675 : {
676 1441970 : if (nCount == -1)
677 276 : Count();
678 :
679 1441970 : if (i < 0 || i >= nCount)
680 50 : return nullptr;
681 :
682 1441920 : return papszList[i];
683 : }
684 :
685 694295 : const char *CPLStringList::operator[](int i) const
686 :
687 : {
688 694295 : if (nCount == -1)
689 512 : Count();
690 :
691 694295 : if (i < 0 || i >= nCount)
692 2 : return nullptr;
693 :
694 694293 : return papszList[i];
695 : }
696 :
697 : /************************************************************************/
698 : /* StealList() */
699 : /************************************************************************/
700 :
701 : /**
702 : * Seize ownership of underlying string array.
703 : *
704 : * This method is similar to List(), except that the returned list is
705 : * now owned by the caller and the CPLStringList is emptied.
706 : *
707 : * @return the C style string list.
708 : */
709 1605960 : char **CPLStringList::StealList()
710 :
711 : {
712 1605960 : char **papszRetList = papszList;
713 :
714 1605960 : bOwnList = false;
715 1605960 : papszList = nullptr;
716 1605960 : nCount = 0;
717 1605960 : nAllocation = 0;
718 :
719 1605960 : return papszRetList;
720 : }
721 :
722 : /* Case insensitive comparison function */
723 747772 : static int CPLCompareKeyValueString(const char *pszKVa, const char *pszKVb)
724 : {
725 747772 : const char *pszItera = pszKVa;
726 747772 : const char *pszIterb = pszKVb;
727 : while (true)
728 : {
729 5054520 : char cha = *pszItera;
730 5054520 : char chb = *pszIterb;
731 5054520 : if (cha == '=' || cha == '\0')
732 : {
733 4085 : if (chb == '=' || chb == '\0')
734 2 : return 0;
735 : else
736 4083 : return -1;
737 : }
738 5050440 : if (chb == '=' || chb == '\0')
739 : {
740 6413 : return 1;
741 : }
742 5044020 : if (cha >= 'a' && cha <= 'z')
743 504721 : cha -= ('a' - 'A');
744 5044020 : if (chb >= 'a' && chb <= 'z')
745 505560 : chb -= ('a' - 'A');
746 5044020 : if (cha < chb)
747 443040 : return -1;
748 4600980 : else if (cha > chb)
749 294234 : return 1;
750 4306750 : pszItera++;
751 4306750 : pszIterb++;
752 4306750 : }
753 : }
754 :
755 : /************************************************************************/
756 : /* Sort() */
757 : /************************************************************************/
758 :
759 : /**
760 : * Sort the entries in the list and mark list sorted.
761 : *
762 : * Note that once put into "sorted" mode, the CPLStringList will attempt to
763 : * keep things in sorted order through calls to AddString(),
764 : * AddStringDirectly(), AddNameValue(), SetNameValue(). Complete list
765 : * assignments (via Assign() and operator= will clear the sorting state.
766 : * When in sorted order FindName(), FetchNameValue() and FetchNameValueDef()
767 : * will do a binary search to find the key, substantially improve lookup
768 : * performance in large lists.
769 : */
770 :
771 98952 : CPLStringList &CPLStringList::Sort()
772 :
773 : {
774 98952 : Count();
775 98952 : if (!MakeOurOwnCopy())
776 0 : return *this;
777 :
778 98952 : if (nCount > 1)
779 : {
780 5594 : std::sort(papszList, papszList + nCount,
781 697808 : [](const char *a, const char *b)
782 697808 : { return CPLCompareKeyValueString(a, b) < 0; });
783 : }
784 98952 : bIsSorted = true;
785 :
786 98952 : return *this;
787 : }
788 :
789 : /************************************************************************/
790 : /* FindName() */
791 : /************************************************************************/
792 :
793 : /**
794 : * Get index of given name/value keyword.
795 : *
796 : * Note that this search is for a line in the form name=value or name:value.
797 : * Use FindString() or PartialFindString() for searches not based on name=value
798 : * pairs.
799 : *
800 : * @param pszKey the name to search for.
801 : *
802 : * @return the string list index of this name, or -1 on failure.
803 : */
804 :
805 18593500 : int CPLStringList::FindName(const char *pszKey) const
806 :
807 : {
808 18593500 : if (!IsSorted())
809 18571400 : return CSLFindName(papszList, pszKey);
810 :
811 : // If we are sorted, we can do an optimized binary search.
812 21473 : int iStart = 0;
813 21473 : int iEnd = nCount - 1;
814 21473 : size_t nKeyLen = strlen(pszKey);
815 :
816 53289 : while (iStart <= iEnd)
817 : {
818 39195 : const int iMiddle = (iEnd + iStart) / 2;
819 39195 : const char *pszMiddle = papszList[iMiddle];
820 :
821 39195 : if (EQUALN(pszMiddle, pszKey, nKeyLen) &&
822 7767 : (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':'))
823 7379 : return iMiddle;
824 :
825 31816 : if (CPLCompareKeyValueString(pszKey, pszMiddle) < 0)
826 8763 : iEnd = iMiddle - 1;
827 : else
828 23053 : iStart = iMiddle + 1;
829 : }
830 :
831 14094 : return -1;
832 : }
833 :
834 : /************************************************************************/
835 : /* FetchBool() */
836 : /************************************************************************/
837 : /**
838 : *
839 : * Check for boolean key value.
840 : *
841 : * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
842 : * with the given name, and if it can be interpreted as being TRUE. If
843 : * the key appears without any "=Value" portion it will be considered true.
844 : * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
845 : * if the key appears in the list it will be considered TRUE. If the key
846 : * doesn't appear at all, the indicated default value will be returned.
847 : *
848 : * @param pszKey the key value to look for (case insensitive).
849 : * @param bDefault the value to return if the key isn't found at all.
850 : *
851 : * @return true or false
852 : */
853 :
854 13713 : bool CPLStringList::FetchBool(const char *pszKey, bool bDefault) const
855 :
856 : {
857 13713 : const char *pszValue = FetchNameValue(pszKey);
858 :
859 13709 : if (pszValue == nullptr)
860 13461 : return bDefault;
861 :
862 248 : return CPLTestBool(pszValue);
863 : }
864 :
865 : /************************************************************************/
866 : /* FetchBoolean() */
867 : /************************************************************************/
868 : /**
869 : *
870 : * DEPRECATED: Check for boolean key value.
871 : *
872 : * In a CPLStringList of "Name=Value" pairs, look to see if there is a key
873 : * with the given name, and if it can be interpreted as being TRUE. If
874 : * the key appears without any "=Value" portion it will be considered true.
875 : * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
876 : * if the key appears in the list it will be considered TRUE. If the key
877 : * doesn't appear at all, the indicated default value will be returned.
878 : *
879 : * @param pszKey the key value to look for (case insensitive).
880 : * @param bDefault the value to return if the key isn't found at all.
881 : *
882 : * @return TRUE or FALSE
883 : */
884 :
885 2997 : int CPLStringList::FetchBoolean(const char *pszKey, int bDefault) const
886 :
887 : {
888 2997 : return FetchBool(pszKey, CPL_TO_BOOL(bDefault)) ? TRUE : FALSE;
889 : }
890 :
891 : /************************************************************************/
892 : /* FetchNameValue() */
893 : /************************************************************************/
894 :
895 : /**
896 : * Fetch value associated with this key name.
897 : *
898 : * If this list sorted, a fast binary search is done, otherwise a linear
899 : * scan is done. Name lookup is case insensitive.
900 : *
901 : * @param pszName the key name to search for.
902 : *
903 : * @return the corresponding value or NULL if not found. The returned string
904 : * should not be modified and points into internal object state that may
905 : * change on future calls.
906 : */
907 :
908 13175500 : const char *CPLStringList::FetchNameValue(const char *pszName) const
909 :
910 : {
911 13175500 : const int iKey = FindName(pszName);
912 :
913 13191700 : if (iKey == -1)
914 4729660 : return nullptr;
915 :
916 8462060 : CPLAssert(papszList[iKey][strlen(pszName)] == '=' ||
917 : papszList[iKey][strlen(pszName)] == ':');
918 :
919 8462060 : return papszList[iKey] + strlen(pszName) + 1;
920 : }
921 :
922 : /************************************************************************/
923 : /* FetchNameValueDef() */
924 : /************************************************************************/
925 :
926 : /**
927 : * Fetch value associated with this key name.
928 : *
929 : * If this list sorted, a fast binary search is done, otherwise a linear
930 : * scan is done. Name lookup is case insensitive.
931 : *
932 : * @param pszName the key name to search for.
933 : * @param pszDefault the default value returned if the named entry isn't found.
934 : *
935 : * @return the corresponding value or the passed default if not found.
936 : */
937 :
938 38321 : const char *CPLStringList::FetchNameValueDef(const char *pszName,
939 : const char *pszDefault) const
940 :
941 : {
942 38321 : const char *pszValue = FetchNameValue(pszName);
943 38320 : if (pszValue == nullptr)
944 26904 : return pszDefault;
945 :
946 11416 : return pszValue;
947 : }
948 :
949 : /************************************************************************/
950 : /* InsertString() */
951 : /************************************************************************/
952 :
953 : /**
954 : * \fn CPLStringList *CPLStringList::InsertString( int nInsertAtLineNo,
955 : * const char *pszNewLine );
956 : *
957 : * \brief Insert into the list at identified location.
958 : *
959 : * This method will insert a string into the list at the identified
960 : * location. The insertion point must be within or at the end of the list.
961 : * The following entries are pushed down to make space.
962 : *
963 : * @param nInsertAtLineNo the line to insert at, zero to insert at front.
964 : * @param pszNewLine to the line to insert. This string will be copied.
965 : */
966 :
967 : /************************************************************************/
968 : /* InsertStringDirectly() */
969 : /************************************************************************/
970 :
971 : /**
972 : * Insert into the list at identified location.
973 : *
974 : * This method will insert a string into the list at the identified
975 : * location. The insertion point must be within or at the end of the list.
976 : * The following entries are pushed down to make space.
977 : *
978 : * @param nInsertAtLineNo the line to insert at, zero to insert at front.
979 : * @param pszNewLine to the line to insert, the ownership of this string
980 : * will be taken over the by the object. It must have been allocated on the
981 : * heap.
982 : */
983 :
984 7018 : CPLStringList &CPLStringList::InsertStringDirectly(int nInsertAtLineNo,
985 : char *pszNewLine)
986 :
987 : {
988 7018 : if (nCount == -1)
989 27 : Count();
990 :
991 7018 : if (!EnsureAllocation(nCount + 1))
992 : {
993 0 : VSIFree(pszNewLine);
994 0 : return *this;
995 : }
996 :
997 7018 : if (nInsertAtLineNo < 0 || nInsertAtLineNo > nCount)
998 : {
999 0 : CPLError(CE_Failure, CPLE_AppDefined,
1000 : "CPLStringList::InsertString() requested beyond list end.");
1001 0 : return *this;
1002 : }
1003 :
1004 7018 : bIsSorted = false;
1005 :
1006 22729 : for (int i = nCount; i > nInsertAtLineNo; i--)
1007 15711 : papszList[i] = papszList[i - 1];
1008 :
1009 7018 : papszList[nInsertAtLineNo] = pszNewLine;
1010 7018 : papszList[++nCount] = nullptr;
1011 :
1012 7018 : return *this;
1013 : }
1014 :
1015 : /************************************************************************/
1016 : /* RemoveStrings() */
1017 : /************************************************************************/
1018 :
1019 : /**
1020 : * Remove strings inside a CPLStringList.
1021 : *
1022 : * @param nFirstLineToDelete the 0-based index of the first string to
1023 : * remove. If this value is -1 or is larger than the actual
1024 : * number of strings in list then the nNumToRemove last strings are
1025 : * removed.
1026 : * @param nNumToRemove the number of strings to remove
1027 : *
1028 : * @return a reference to the CPLStringList on which it was invoked.
1029 : * @since 3.13
1030 : */
1031 1 : CPLStringList &CPLStringList::RemoveStrings(int nFirstLineToDelete,
1032 : int nNumToRemove)
1033 : {
1034 1 : if (!MakeOurOwnCopy())
1035 0 : return *this;
1036 :
1037 1 : papszList =
1038 1 : CSLRemoveStrings(papszList, nFirstLineToDelete, nNumToRemove, nullptr);
1039 1 : nCount = -1;
1040 1 : return *this;
1041 : }
1042 :
1043 : /************************************************************************/
1044 : /* FindSortedInsertionPoint() */
1045 : /* */
1046 : /* Find the location at which the indicated line should be */
1047 : /* inserted in order to keep things in sorted order. */
1048 : /************************************************************************/
1049 :
1050 6625 : int CPLStringList::FindSortedInsertionPoint(const char *pszLine)
1051 :
1052 : {
1053 6625 : CPLAssert(IsSorted());
1054 :
1055 6625 : int iStart = 0;
1056 6625 : int iEnd = nCount - 1;
1057 :
1058 19621 : while (iStart <= iEnd)
1059 : {
1060 12996 : const int iMiddle = (iEnd + iStart) / 2;
1061 12996 : const char *pszMiddle = papszList[iMiddle];
1062 :
1063 12996 : if (CPLCompareKeyValueString(pszLine, pszMiddle) < 0)
1064 2131 : iEnd = iMiddle - 1;
1065 : else
1066 10865 : iStart = iMiddle + 1;
1067 : }
1068 :
1069 6625 : iEnd++;
1070 6625 : CPLAssert(iEnd >= 0 && iEnd <= nCount);
1071 6625 : CPLAssert(iEnd == 0 ||
1072 : CPLCompareKeyValueString(pszLine, papszList[iEnd - 1]) >= 0);
1073 6625 : CPLAssert(iEnd == nCount ||
1074 : CPLCompareKeyValueString(pszLine, papszList[iEnd]) <= 0);
1075 :
1076 6625 : return iEnd;
1077 : }
1078 :
1079 : namespace cpl
1080 : {
1081 :
1082 : /************************************************************************/
1083 : /* CSLIterator::operator==(const CSLIterator &other) */
1084 : /************************************************************************/
1085 :
1086 : /*! @cond Doxygen_Suppress */
1087 21943100 : bool CSLIterator::operator==(const CSLIterator &other) const
1088 : {
1089 21943100 : if (!m_bAtEnd && other.m_bAtEnd)
1090 : {
1091 21943200 : return m_papszList == nullptr || *m_papszList == nullptr;
1092 : }
1093 0 : if (!m_bAtEnd && !other.m_bAtEnd)
1094 : {
1095 0 : return m_papszList == other.m_papszList;
1096 : }
1097 0 : if (m_bAtEnd && other.m_bAtEnd)
1098 : {
1099 0 : return true;
1100 : }
1101 0 : return false;
1102 : }
1103 :
1104 : /*! @endcond */
1105 :
1106 : /************************************************************************/
1107 : /* CSLNameValueIterator::operator*() */
1108 : /************************************************************************/
1109 :
1110 : /*! @cond Doxygen_Suppress */
1111 6134 : CSLNameValueIterator::value_type CSLNameValueIterator::operator*()
1112 : {
1113 6134 : if (m_papszList)
1114 : {
1115 6135 : while (*m_papszList)
1116 : {
1117 6135 : char *pszKey = nullptr;
1118 6135 : const char *pszValue = CPLParseNameValue(*m_papszList, &pszKey);
1119 6135 : if (pszKey)
1120 : {
1121 6132 : m_osKey = pszKey;
1122 6132 : CPLFree(pszKey);
1123 6132 : return {m_osKey.c_str(), pszValue};
1124 : }
1125 3 : else if (m_bReturnNullKeyIfNotNameValue)
1126 : {
1127 2 : return {nullptr, *m_papszList};
1128 : }
1129 : // Skip entries that are not name=value pairs.
1130 1 : ++m_papszList;
1131 : }
1132 : }
1133 : // Should not happen
1134 0 : CPLAssert(false);
1135 : return {"", ""};
1136 : }
1137 :
1138 : /*! @endcond */
1139 :
1140 : /************************************************************************/
1141 : /* CSLNameValueIteratorWrapper::end() */
1142 : /************************************************************************/
1143 :
1144 : /*! @cond Doxygen_Suppress */
1145 12953 : CSLNameValueIterator CSLNameValueIteratorWrapper::end() const
1146 : {
1147 12953 : int nCount = CSLCount(m_papszList);
1148 12953 : if (!m_bReturnNullKeyIfNotNameValue)
1149 : {
1150 12856 : while (nCount > 0 && strchr(m_papszList[nCount - 1], '=') == nullptr)
1151 12 : --nCount;
1152 : }
1153 12953 : return CSLNameValueIterator{m_papszList + nCount,
1154 12953 : m_bReturnNullKeyIfNotNameValue};
1155 : }
1156 :
1157 : /*! @endcond */
1158 :
1159 : } // namespace cpl
|