VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_core / text / juce_StringPairArray.cpp
blobecc0c82b646ec42edca593428b11455f3161b4f7
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
20 ==============================================================================
23 namespace juce
26 StringPairArray::StringPairArray (bool shouldIgnoreCase) : ignoreCase (shouldIgnoreCase)
30 StringPairArray::StringPairArray (const StringPairArray& other)
31 : keys (other.keys),
32 values (other.values),
33 ignoreCase (other.ignoreCase)
37 StringPairArray& StringPairArray::operator= (const StringPairArray& other)
39 keys = other.keys;
40 values = other.values;
41 return *this;
44 bool StringPairArray::operator== (const StringPairArray& other) const
46 auto num = size();
48 if (num != other.size())
49 return false;
51 for (int i = 0; i < num; ++i)
53 if (keys[i] == other.keys[i]) // optimise for the case where the keys are in the same order
55 if (values[i] != other.values[i])
56 return false;
58 else
60 // if we encounter keys that are in a different order, search remaining items by brute force..
61 for (int j = i; j < num; ++j)
63 auto otherIndex = other.keys.indexOf (keys[j], other.ignoreCase);
65 if (otherIndex < 0 || values[j] != other.values[otherIndex])
66 return false;
69 return true;
73 return true;
76 bool StringPairArray::operator!= (const StringPairArray& other) const
78 return ! operator== (other);
81 const String& StringPairArray::operator[] (StringRef key) const
83 return values[keys.indexOf (key, ignoreCase)];
86 String StringPairArray::getValue (StringRef key, const String& defaultReturnValue) const
88 auto i = keys.indexOf (key, ignoreCase);
90 if (i >= 0)
91 return values[i];
93 return defaultReturnValue;
96 bool StringPairArray::containsKey (StringRef key) const noexcept
98 return keys.contains (key, ignoreCase);
101 void StringPairArray::set (const String& key, const String& value)
103 auto i = keys.indexOf (key, ignoreCase);
105 if (i >= 0)
107 values.set (i, value);
109 else
111 keys.add (key);
112 values.add (value);
116 void StringPairArray::addArray (const StringPairArray& other)
118 for (int i = 0; i < other.size(); ++i)
119 set (other.keys[i], other.values[i]);
122 void StringPairArray::clear()
124 keys.clear();
125 values.clear();
128 void StringPairArray::remove (StringRef key)
130 remove (keys.indexOf (key, ignoreCase));
133 void StringPairArray::remove (int index)
135 keys.remove (index);
136 values.remove (index);
139 void StringPairArray::setIgnoresCase (bool shouldIgnoreCase)
141 ignoreCase = shouldIgnoreCase;
144 bool StringPairArray::getIgnoresCase() const noexcept
146 return ignoreCase;
149 String StringPairArray::getDescription() const
151 String s;
153 for (int i = 0; i < keys.size(); ++i)
155 s << keys[i] << " = " << values[i];
157 if (i < keys.size())
158 s << ", ";
161 return s;
164 void StringPairArray::minimiseStorageOverheads()
166 keys.minimiseStorageOverheads();
167 values.minimiseStorageOverheads();
170 template <typename Map>
171 void StringPairArray::addMapImpl (const Map& toAdd)
173 // If we just called `set` for each item in `toAdd`, that would
174 // perform badly when adding to large StringPairArrays, as `set`
175 // has to loop through the whole container looking for matching keys.
176 // Instead, we use a temporary map to give us better lookup performance.
177 std::map<String, int> contents;
179 const auto normaliseKey = [this] (const String& key)
181 return ignoreCase ? key.toLowerCase() : key;
184 for (auto i = 0; i != size(); ++i)
185 contents.emplace (normaliseKey (getAllKeys().getReference (i)), i);
187 for (const auto& pair : toAdd)
189 const auto key = normaliseKey (pair.first);
190 const auto it = contents.find (key);
192 if (it != contents.cend())
194 values.getReference (it->second) = pair.second;
196 else
198 contents.emplace (key, static_cast<int> (contents.size()));
199 keys.add (pair.first);
200 values.add (pair.second);
205 void StringPairArray::addUnorderedMap (const std::unordered_map<String, String>& toAdd) { addMapImpl (toAdd); }
206 void StringPairArray::addMap (const std::map<String, String>& toAdd) { addMapImpl (toAdd); }
208 //==============================================================================
209 //==============================================================================
210 #if JUCE_UNIT_TESTS
212 static String operator""_S (const char* chars, size_t)
214 return String { chars };
217 class StringPairArrayTests : public UnitTest
219 public:
220 StringPairArrayTests()
221 : UnitTest ("StringPairArray", UnitTestCategories::text)
224 void runTest() override
226 beginTest ("addMap respects case sensitivity of StringPairArray");
228 StringPairArray insensitive { true };
229 insensitive.addMap ({ { "duplicate", "a" },
230 { "Duplicate", "b" } });
232 expect (insensitive.size() == 1);
233 expectEquals (insensitive["DUPLICATE"], "a"_S);
235 StringPairArray sensitive { false };
236 sensitive.addMap ({ { "duplicate", "a"_S },
237 { "Duplicate", "b"_S } });
239 expect (sensitive.size() == 2);
240 expectEquals (sensitive["duplicate"], "a"_S);
241 expectEquals (sensitive["Duplicate"], "b"_S);
242 expectEquals (sensitive["DUPLICATE"], ""_S);
245 beginTest ("addMap overwrites existing pairs");
247 StringPairArray insensitive { true };
248 insensitive.set ("key", "value");
249 insensitive.addMap ({ { "KEY", "VALUE" } });
251 expect (insensitive.size() == 1);
252 expectEquals (insensitive.getAllKeys()[0], "key"_S);
253 expectEquals (insensitive.getAllValues()[0], "VALUE"_S);
255 StringPairArray sensitive { false };
256 sensitive.set ("key", "value");
257 sensitive.addMap ({ { "KEY", "VALUE" },
258 { "key", "another value" } });
260 expect (sensitive.size() == 2);
261 expect (sensitive.getAllKeys() == StringArray { "key", "KEY" });
262 expect (sensitive.getAllValues() == StringArray { "another value", "VALUE" });
265 beginTest ("addMap doesn't change the order of existing keys");
267 StringPairArray array;
268 array.set ("a", "a");
269 array.set ("z", "z");
270 array.set ("b", "b");
271 array.set ("y", "y");
272 array.set ("c", "c");
274 array.addMap ({ { "B", "B" },
275 { "0", "0" },
276 { "Z", "Z" } });
278 expect (array.getAllKeys() == StringArray { "a", "z", "b", "y", "c", "0" });
279 expect (array.getAllValues() == StringArray { "a", "Z", "B", "y", "c", "0" });
282 beginTest ("addMap has equivalent behaviour to addArray");
284 StringPairArray initial;
285 initial.set ("aaa", "aaa");
286 initial.set ("zzz", "zzz");
287 initial.set ("bbb", "bbb");
289 auto withAddMap = initial;
290 withAddMap.addMap ({ { "ZZZ", "ZZZ" },
291 { "ddd", "ddd" } });
293 auto withAddArray = initial;
294 withAddArray.addArray ([]
296 StringPairArray toAdd;
297 toAdd.set ("ZZZ", "ZZZ");
298 toAdd.set ("ddd", "ddd");
299 return toAdd;
300 }());
302 expect (withAddMap == withAddArray);
307 static StringPairArrayTests stringPairArrayTests;
309 #endif
311 } // namespace juce