1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 #include <tools/gen.hxx>
29 #include <tools/debug.hxx>
30 #include <soldep/hashtbl.hxx>
32 // -------------------------------------------------------------
37 enum ETag
{ TAG_EMPTY
, TAG_USED
, TAG_DELETED
};
44 HashItem() { m_Tag
= TAG_EMPTY
; m_pObject
= NULL
; }
46 BOOL
IsDeleted() const
47 { return m_Tag
== TAG_DELETED
; }
50 { return m_Tag
== TAG_DELETED
|| m_Tag
== TAG_EMPTY
; }
53 { return m_Tag
== TAG_EMPTY
; }
56 { return m_Tag
== TAG_USED
; }
59 { m_Tag
= TAG_DELETED
; m_Key
= ""; m_pObject
= NULL
; }
61 ByteString
const& GetKey() const
64 void* GetObject() const
67 void SetObject(ByteString
const Key
, void *pObject
)
68 { m_Tag
= TAG_USED
; m_Key
= Key
; m_pObject
= pObject
; }
71 #define MIN(a,b) (a)<(b)?(a):(b)
72 #define MAX(a,b) (a)>(b)?(a):(b)
74 // -------------------------------------------------------------
78 /*static*/ double HashTable::m_defMaxLoadFactor
= 0.8;
79 /*static*/ double HashTable::m_defDefGrowFactor
= 2.0;
81 HashTable::HashTable(ULONG lSize
, BOOL bOwner
, double dMaxLoadFactor
, double dGrowFactor
)
86 m_dMaxLoadFactor
= MAX(0.5,MIN(1.0,dMaxLoadFactor
)); // 0.5 ... 1.0
87 m_dGrowFactor
= MAX(1.3,MIN(5.0,dGrowFactor
)); // 1.3 ... 5.0
88 m_pData
= new HashItem
[lSize
];
92 m_aStatistic
.m_lSingleHash
= 0;
93 m_aStatistic
.m_lDoubleHash
= 0;
94 m_aStatistic
.m_lProbe
= 0;
98 HashTable::~HashTable()
100 // Wenn die HashTable der Owner der Objecte ist,
101 // müssen die Destruktoren separat gerufen werden.
102 // Dies geschieht über die virtuelle Methode OnDeleteObject()
104 // Problem: Virtuelle Funktionen sind im Destructor nicht virtuell!!
105 // Der Code muß deshalb ins Macro
110 for (ULONG i=0; i<GetSize(); i++)
112 void *pObject = GetObjectAt(i);
115 OnDeleteObject(pObject());
120 // Speicher für HashItems freigeben
124 void* HashTable::GetObjectAt(ULONG lPos
) const
125 // Gibt Objekt zurück, wenn es eines gibt, sonst NULL;
127 DBG_ASSERT(lPos
<m_lSize
, "HashTable::GetObjectAt()");
129 HashItem
*pItem
= &m_pData
[lPos
];
131 return pItem
->IsUsed() ? pItem
->GetObject() : NULL
;
134 void HashTable::OnDeleteObject(void*)
136 DBG_ERROR("HashTable::OnDeleteObject(void*) nicht überladen");
139 ULONG
HashTable::Hash(ByteString
const& Key
) const
145 for (i=0,n=Key.Len(); i<n; i++)
148 lHash += (ULONG)(USHORT)Key.GetStr()[i];
154 // Hashfunktion von P.J. Weinberger
155 // aus dem "Drachenbuch" von Aho/Sethi/Ullman
160 for (i
=0,n
=Key
.Len(); i
<n
; i
++)
162 h
= (h
<<4) + (ULONG
)(USHORT
)Key
.GetBuffer()[i
];
175 ULONG
HashTable::DHash(ByteString
const& Key
, ULONG lOldHash
) const
177 ULONG lHash
= lOldHash
;
180 for (i
=0,n
=Key
.Len(); i
<n
; i
++)
183 lHash
+= (ULONG
)(USHORT
)Key
.GetBuffer()[i
];
191 + (char)Key.GetStr()[0] * 256
192 + (char)Key.GetStr()[Key.Len()-1]
199 ULONG
HashTable::Probe(ULONG lPos
) const
200 // gibt den Folgewert von lPos zurück
202 lPos
++; if (lPos
==m_lSize
) lPos
=0;
206 BOOL
HashTable::IsFull() const
208 return m_lElem
>=m_lSize
;
211 BOOL
HashTable::Insert(ByteString
const& Key
, void* pObject
)
212 // pre: Key ist nicht im Dictionary enthalten, sonst return FALSE
213 // Dictionary ist nicht voll, sonst return FALSE
214 // post: pObject ist unter Key im Dictionary; m_nElem wurde erhöht
220 DBG_ERROR("HashTable::Insert() is full");
224 if (FindPos(Key
) != NULL
)
227 ULONG lPos
= Hash(Key
);
228 HashItem
*pItem
= &m_pData
[lPos
];
232 if (pItem
->IsEmpty())
234 pItem
->SetObject(Key
, pObject
);
238 m_aStatistic
.m_lSingleHash
++;
246 lPos
= DHash(Key
,lPos
);
247 pItem
= &m_pData
[lPos
];
249 if (pItem
->IsEmpty())
251 pItem
->SetObject(Key
, pObject
);
255 m_aStatistic
.m_lDoubleHash
++;
266 m_aStatistic
.m_lProbe
++;
270 pItem
= &m_pData
[lPos
];
272 while(!pItem
->IsEmpty());
274 pItem
->SetObject(Key
, pObject
);
279 HashItem
* HashTable::FindPos(ByteString
const& Key
) const
280 // sucht den Key; gibt Refrenz auf den Eintrag (gefunden)
281 // oder NULL (nicht gefunden) zurück
288 ULONG lPos
= Hash(Key
);
289 HashItem
*pItem
= &m_pData
[lPos
];
292 && pItem
->GetKey() == Key
)
299 if (pItem
->IsDeleted() || pItem
->IsUsed())
301 lPos
= DHash(Key
,lPos
);
302 pItem
= &m_pData
[lPos
];
305 && pItem
->GetKey() == Key
)
312 if (pItem
->IsDeleted() || pItem
->IsUsed())
322 pItem
= &m_pData
[lPos
];
324 bFound
= pItem
->IsUsed()
325 && pItem
->GetKey() == Key
;
327 bEnd
= !(n
<m_lSize
|| pItem
->IsFree());
329 while(!bFound
&& !bEnd
);
331 return bFound
? pItem
: NULL
;
340 void* HashTable::Find(ByteString
const& Key
) const
341 // Gibt Verweis des Objektes zurück, das unter Key abgespeichert ist,
342 // oder NULL wenn nicht vorhanden.
347 HashItem
*pItem
= FindPos(Key
);
350 && pItem
->GetKey() == Key
)
351 return pItem
->GetObject();
356 void* HashTable::Delete(ByteString
const& Key
)
357 // Löscht Objekt, das unter Key abgespeichert ist und gibt Verweis
359 // Gibt NULL zurück, wenn Key nicht vorhanden ist.
362 // post: Objekt ist nicht mehr enthalten; m_lElem dekrementiert
363 // Wenn die HashTable der Owner ist, wurde das Object gelöscht
365 HashItem
*pItem
= FindPos(Key
);
368 && pItem
->GetKey() == Key
)
370 void* pObject
= pItem
->GetObject();
373 OnDeleteObject(pObject
);
385 double HashTable::CalcLoadFactor() const
386 // prozentuale Belegung der Hashtabelle berechnen
388 return double(m_lElem
) / double(m_lSize
);
391 void HashTable::SmartGrow()
392 // Achtung: da die Objekte umkopiert werden, darf die OnDeleteObject-Methode
393 // nicht gerufen werden
395 double dLoadFactor
= CalcLoadFactor();
397 if (dLoadFactor
<= m_dMaxLoadFactor
)
398 return; // nothing to grow
400 ULONG lOldSize
= m_lSize
; // alte Daten sichern
401 HashItem
* pOldData
= m_pData
;
403 m_lSize
= ULONG (m_dGrowFactor
* m_lSize
); // neue Größe
404 m_pData
= new HashItem
[m_lSize
]; // neue Daten holen
407 // Zustand "Tabelle voll" wird in Insert abgefangen
416 m_lElem
= 0; // noch keine neuen Daten
418 // Umkopieren der Daten
420 for (ULONG i
=0; i
<lOldSize
; i
++)
422 HashItem
*pItem
= &pOldData
[i
];
425 Insert(pItem
->GetKey(),pItem
->GetObject());
431 // Iterator ---------------------------------------------------------
434 HashTableIterator::HashTableIterator(HashTable
const& aTable
)
440 void* HashTableIterator::GetFirst()
443 return FindValidObject(TRUE
/* forward */);
446 void* HashTableIterator::GetLast()
448 m_lAt
= m_aTable
.GetSize() -1;
449 return FindValidObject(FALSE
/* backward */);
452 void* HashTableIterator::GetNext()
454 if (m_lAt
+1 >= m_aTable
.GetSize())
458 return FindValidObject(TRUE
/* forward */);
461 void* HashTableIterator::GetPrev()
467 return FindValidObject(FALSE
/* backward */);
470 void* HashTableIterator::FindValidObject(BOOL bForward
)
471 // Sucht nach einem vorhandenen Objekt ab der aktuellen
474 // pre: ab inkl. m_lAt soll die Suche beginnen
475 // post: if not found then
476 // if bForward == TRUE then
477 // m_lAt == m_aTable.GetSize() -1
481 // m_lAt ist die gefundene Position
483 void *pObject
= m_aTable
.GetObjectAt(m_lAt
);
488 while (pObject
== NULL
489 && (bForward
? ((m_lAt
+1) < m_aTable
.GetSize())
497 pObject
= m_aTable
.GetObjectAt(m_lAt
);
504 DBG_ASSERT(bForward
? m_lAt
== m_aTable
.GetSize() -1 : m_lAt
== 0,
505 "HashTableIterator::FindValidObject()");