Skip to content

Commit 82df115

Browse files
authored
Make DDF manufacturer name matching case insensitive (#7864)
This fixes a regression from v2.27.0-beta. A DDF should match regardless if the manufacturer name is given as "HEIMAN", "Heiman", "heiman", etc. Note this applies only to plain ASCII strings. For manufacturer names which contain multibyte UTF-8 characters the DDF must have the exact name representation. This should by fine for 99% of all cases.
1 parent e7707d6 commit 82df115

File tree

2 files changed

+105
-13
lines changed

2 files changed

+105
-13
lines changed

de_web_plugin.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16749,19 +16749,23 @@ void DEV_AllocateGroup(const Device *device, Resource *rsub, ResourceItem *item)
1674916749
*/
1675016750
void DEV_ReloadDeviceIdendifier(unsigned atomIndexMfname, unsigned atomIndexModelid)
1675116751
{
16752+
(void)atomIndexMfname;
16753+
1675216754
for (auto &dev : plugin->m_devices)
1675316755
{
1675416756
{
16755-
const ResourceItem *mfname = dev->item(RAttrManufacturerName);
16756-
if (!mfname || mfname->atomIndex() != atomIndexMfname)
16757+
const ResourceItem *modelid = dev->item(RAttrModelId);
16758+
if (!modelid || modelid->atomIndex() != atomIndexModelid)
1675716759
continue;
1675816760
}
1675916761

16762+
#if 0 // ignore for now since manufacturer name might have case sensitive variations
1676016763
{
16761-
const ResourceItem *modelid = dev->item(RAttrModelId);
16762-
if (!modelid || modelid->atomIndex() != atomIndexModelid)
16764+
const ResourceItem *mfname = dev->item(RAttrManufacturerName);
16765+
if (!mfname || mfname->atomIndex() != atomIndexMfname)
1676316766
continue;
1676416767
}
16768+
#endif
1676516769

1676616770
enqueueEvent(Event(RDevices, REventDDFReload, 0, dev->key()));
1676716771
}

device_descriptions.cpp

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ struct DDF_LoadRecord
121121
{
122122
AT_AtomIndex modelid;
123123
AT_AtomIndex mfname;
124+
uint32_t mfnameLowerCaseHash;
124125
DDF_LoadState loadState;
125126
};
126127

@@ -166,6 +167,66 @@ static void DDF_UpdateItemHandles(std::vector<DeviceDescription> &descriptions,
166167
static void DDF_TryCompileAndFixJavascript(QString *expr, const QString &path);
167168
DeviceDescription DDF_LoadScripts(const DeviceDescription &ddf);
168169

170+
/*
171+
* https://maskray.me/blog/2023-04-12-elf-hash-function
172+
*
173+
* PJW hash adapted from musl libc.
174+
*
175+
* TODO(mpi): make this a own module U_StringHash()
176+
*/
177+
static uint32_t DDF_StringHash(const void *s0, unsigned size)
178+
{
179+
uint32_t h;
180+
const unsigned char *s;
181+
182+
h = 0;
183+
s = (const unsigned char*)s0;
184+
185+
while (size--)
186+
{
187+
h = 16 * h + *s++;
188+
h ^= h >> 24 & 0xF0;
189+
}
190+
return h & 0xfffffff;
191+
}
192+
193+
/*! Helper to create a 32-bit string hash from an atom string.
194+
195+
This is mainly used to get a unique number to compare case insensitive manufacturer names.
196+
For example for "HEIMAN", "heiman" and "Heiman" atoms this function returns the same hash.
197+
*/
198+
static uint32_t DDF_AtomLowerCaseStringHash(AT_AtomIndex ati)
199+
{
200+
unsigned len;
201+
AT_Atom atom;
202+
char str[192];
203+
204+
str[0] = '\0';
205+
atom = AT_GetAtomByIndex(ati);
206+
207+
if (atom.len == 0)
208+
return 0;
209+
210+
if (sizeof(str) <= atom.len) // should not happen
211+
return DDF_StringHash(atom.data, atom.len);
212+
213+
for (len = 0; len < atom.len; len++)
214+
{
215+
uint8_t ch = atom.data[len];
216+
217+
if (ch & 0x80) // non ASCII UTF-8 string, don't bother
218+
return DDF_StringHash(atom.data, atom.len);
219+
220+
if (ch >= 'A' && ch <= 'Z')
221+
ch += (unsigned char)('a' - 'A');
222+
223+
str[len] = (char)ch;
224+
}
225+
226+
str[len] = '\0';
227+
return DDF_StringHash(str, len);
228+
}
229+
169230
/*! Constructor. */
170231
DeviceDescriptions::DeviceDescriptions(QObject *parent) :
171232
QObject(parent),
@@ -861,6 +922,7 @@ void DeviceDescriptions::prepare()
861922
{
862923
DDF_LoadRecord rec;
863924
rec.mfname.index = res[i].mfnameAtomIndex;
925+
rec.mfnameLowerCaseHash = DDF_AtomLowerCaseStringHash(rec.mfname);
864926
rec.modelid.index = res[i].modelIdAtomIndex;
865927
rec.loadState = DDF_LoadStateScheduled;
866928
records.push_back(rec);
@@ -1089,6 +1151,8 @@ const DeviceDescription &DeviceDescriptions::get(const Resource *resource, DDF_M
10891151
U_ASSERT(modelidAtomIndex != 0);
10901152
U_ASSERT(mfnameAtomIndex != 0);
10911153

1154+
uint32_t mfnameLowerCaseHash = DDF_AtomLowerCaseStringHash(AT_AtomIndex{mfnameAtomIndex});
1155+
10921156
/*
10931157
* Filter matching DDFs, there can be multiple entries for the same modelid and manufacturer name.
10941158
* Further sorting for the 'best' match according to attr/ddf_policy is done afterwards.
@@ -1098,7 +1162,7 @@ const DeviceDescription &DeviceDescriptions::get(const Resource *resource, DDF_M
10981162

10991163
for (;matchedCount < matchedIndices.size();)
11001164
{
1101-
i = std::find_if(i, d->descriptions.end(), [modelidAtomIndex, mfnameAtomIndex](const DeviceDescription &ddf)
1165+
i = std::find_if(i, d->descriptions.end(), [modelidAtomIndex, mfnameAtomIndex, mfnameLowerCaseHash](const DeviceDescription &ddf)
11021166
{
11031167
if (ddf.mfnameAtomIndices.size() != ddf.modelidAtomIndices.size())
11041168
{
@@ -1107,8 +1171,16 @@ const DeviceDescription &DeviceDescriptions::get(const Resource *resource, DDF_M
11071171

11081172
for (size_t j = 0; j < ddf.modelidAtomIndices.size(); j++)
11091173
{
1110-
if (ddf.modelidAtomIndices[j] == modelidAtomIndex && ddf.mfnameAtomIndices[j] == mfnameAtomIndex)
1111-
return true;
1174+
if (ddf.modelidAtomIndices[j] == modelidAtomIndex)
1175+
{
1176+
if (ddf.mfnameAtomIndices[j] == mfnameAtomIndex) // exact manufacturer name match
1177+
return true;
1178+
1179+
// tolower() manufacturer name match
1180+
uint32_t mfnameLowerCaseHash2 = DDF_AtomLowerCaseStringHash(AT_AtomIndex{ddf.mfnameAtomIndices[j]});
1181+
if (mfnameLowerCaseHash == mfnameLowerCaseHash2)
1182+
return true;
1183+
}
11121184
}
11131185

11141186
return false;
@@ -1378,12 +1450,19 @@ bool DeviceDescriptions::loadDDFAndBundlesFromDisc(const Resource *resource)
13781450
return false;
13791451
}
13801452

1453+
uint32_t mfnameLowerCaseHash = DDF_AtomLowerCaseStringHash(AT_AtomIndex{mfnameAtomIndex});
1454+
13811455
for (const DDF_LoadRecord &loadRecord : d->ddfLoadRecords)
13821456
{
1383-
if (loadRecord.mfname.index == mfnameAtomIndex && loadRecord.modelid.index == modelidAtomIndex)
1457+
if (loadRecord.mfnameLowerCaseHash == mfnameLowerCaseHash && loadRecord.modelid.index == modelidAtomIndex)
13841458
{
13851459
return false; // been here before
13861460
}
1461+
1462+
if (loadRecord.mfname.index == mfnameAtomIndex && loadRecord.modelid.index == modelidAtomIndex)
1463+
{
1464+
return false; // been here before, note this check can likely be removed due the lower case check already been hit
1465+
}
13871466
}
13881467

13891468
DBG_Printf(DBG_DDF, "try load DDF from disc for %s -- %s\n", mfnameItem->toCString(), modelidItem->toCString());
@@ -1392,6 +1471,7 @@ bool DeviceDescriptions::loadDDFAndBundlesFromDisc(const Resource *resource)
13921471
DDF_LoadRecord loadRecord;
13931472
loadRecord.modelid.index = modelidAtomIndex;
13941473
loadRecord.mfname.index = mfnameAtomIndex;
1474+
loadRecord.mfnameLowerCaseHash = mfnameLowerCaseHash;
13951475
loadRecord.loadState = DDF_LoadStateScheduled;
13961476
d->ddfLoadRecords.push_back(loadRecord);
13971477

@@ -2166,6 +2246,7 @@ void DeviceDescriptions::readAllRawJson()
21662246
{
21672247
AT_AtomIndex mfnameIndex;
21682248
AT_AtomIndex modelidIndex;
2249+
uint32_t mfnameLowerCaseHash = 0;
21692250

21702251
mfnameIndex.index = 0;
21712252
modelidIndex.index = 0;
@@ -2190,6 +2271,10 @@ void DeviceDescriptions::readAllRawJson()
21902271
continue;
21912272
}
21922273
}
2274+
else
2275+
{
2276+
mfnameLowerCaseHash = DDF_AtomLowerCaseStringHash(mfnameIndex);
2277+
}
21932278
}
21942279

21952280
{
@@ -2204,12 +2289,12 @@ void DeviceDescriptions::readAllRawJson()
22042289
{
22052290
if (modelidIndex.index == d->ddfLoadRecords[k].modelid.index)
22062291
{
2207-
if (mfnameIndex.index == 0)
2292+
if (mfnameLowerCaseHash == 0)
22082293
{
22092294
// ignore for now, in worst case we load a DDF to memory which isn't used
22102295
U_ASSERT(0);
22112296
}
2212-
else if (mfnameIndex.index != d->ddfLoadRecords[k].mfname.index)
2297+
else if (mfnameLowerCaseHash != d->ddfLoadRecords[k].mfnameLowerCaseHash)
22132298
{
22142299
continue;
22152300
}
@@ -2521,14 +2606,16 @@ static int DDF_IsBundleScheduled(DDF_ParseContext *pctx, const char *desc, unsig
25212606
if (!foundAtoms)
25222607
continue;
25232608

2609+
uint32_t mfnameLowerCaseHash = DDF_AtomLowerCaseStringHash(mfname_ati);
2610+
25242611
for (size_t j = 0; j < ddfLoadRecords.size(); j++)
25252612
{
25262613
const DDF_LoadRecord &rec = ddfLoadRecords[j];
25272614

2528-
if (rec.mfname.index != mfname_ati.index)
2615+
if (rec.modelid.index != modelid_ati.index)
25292616
continue;
25302617

2531-
if (rec.modelid.index != modelid_ati.index)
2618+
if (rec.mfnameLowerCaseHash != mfnameLowerCaseHash)
25322619
continue;
25332620

25342621
return 1;
@@ -2569,12 +2656,13 @@ void DeviceDescriptions::reloadAllRawJsonAndBundles(const Resource *resource)
25692656
const ResourceItem *modelidItem = resource->item(RAttrModelId);
25702657
unsigned mfnameAtomIndex = mfnameItem->atomIndex();
25712658
unsigned modelidAtomIndex = modelidItem->atomIndex();
2659+
uint32_t mfnameLowerCaseHash = DDF_AtomLowerCaseStringHash(AT_AtomIndex{mfnameAtomIndex});
25722660

25732661
for (size_t j = 0; j < d_ptr2->ddfLoadRecords.size(); j++)
25742662
{
25752663
DDF_LoadRecord &rec = d_ptr2->ddfLoadRecords[j];
25762664

2577-
if (rec.mfname.index != mfnameAtomIndex)
2665+
if (rec.mfnameLowerCaseHash != mfnameLowerCaseHash)
25782666
continue;
25792667

25802668
if (rec.modelid.index != modelidAtomIndex)

0 commit comments

Comments
 (0)