1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: hashtbl.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include <tools/gen.hxx>
32 #include <tools/debug.hxx>
33 #include <soldep/hashtbl.hxx>
35 // -------------------------------------------------------------
40 enum ETag
{ TAG_EMPTY
, TAG_USED
, TAG_DELETED
};
47 HashItem() { m_Tag
= TAG_EMPTY
; m_pObject
= NULL
; }
49 BOOL
IsDeleted() const
50 { return m_Tag
== TAG_DELETED
; }
53 { return m_Tag
== TAG_DELETED
|| m_Tag
== TAG_EMPTY
; }
56 { return m_Tag
== TAG_EMPTY
; }
59 { return m_Tag
== TAG_USED
; }
62 { m_Tag
= TAG_DELETED
; m_Key
= ""; m_pObject
= NULL
; }
64 ByteString
const& GetKey() const
67 void* GetObject() const
70 void SetObject(ByteString
const Key
, void *pObject
)
71 { m_Tag
= TAG_USED
; m_Key
= Key
; m_pObject
= pObject
; }
74 #define MIN(a,b) (a)<(b)?(a):(b)
75 #define MAX(a,b) (a)>(b)?(a):(b)
77 // -------------------------------------------------------------
81 /*static*/ double HashTable::m_defMaxLoadFactor
= 0.8;
82 /*static*/ double HashTable::m_defDefGrowFactor
= 2.0;
84 HashTable::HashTable(ULONG lSize
, BOOL bOwner
, double dMaxLoadFactor
, double dGrowFactor
)
89 m_dMaxLoadFactor
= MAX(0.5,MIN(1.0,dMaxLoadFactor
)); // 0.5 ... 1.0
90 m_dGrowFactor
= MAX(1.3,MIN(5.0,dGrowFactor
)); // 1.3 ... 5.0
91 m_pData
= new HashItem
[lSize
];
95 m_aStatistic
.m_lSingleHash
= 0;
96 m_aStatistic
.m_lDoubleHash
= 0;
97 m_aStatistic
.m_lProbe
= 0;
101 HashTable::~HashTable()
103 // Wenn die HashTable der Owner der Objecte ist,
104 // müssen die Destruktoren separat gerufen werden.
105 // Dies geschieht über die virtuelle Methode OnDeleteObject()
107 // Problem: Virtuelle Funktionen sind im Destructor nicht virtuell!!
108 // Der Code muß deshalb ins Macro
113 for (ULONG i=0; i<GetSize(); i++)
115 void *pObject = GetObjectAt(i);
118 OnDeleteObject(pObject());
123 // Speicher für HashItems freigeben
127 void* HashTable::GetObjectAt(ULONG lPos
) const
128 // Gibt Objekt zurück, wenn es eines gibt, sonst NULL;
130 DBG_ASSERT(lPos
<m_lSize
, "HashTable::GetObjectAt()");
132 HashItem
*pItem
= &m_pData
[lPos
];
134 return pItem
->IsUsed() ? pItem
->GetObject() : NULL
;
137 void HashTable::OnDeleteObject(void*)
139 DBG_ERROR("HashTable::OnDeleteObject(void*) nicht überladen");
142 ULONG
HashTable::Hash(ByteString
const& Key
) const
148 for (i=0,n=Key.Len(); i<n; i++)
151 lHash += (ULONG)(USHORT)Key.GetStr()[i];
157 // Hashfunktion von P.J. Weinberger
158 // aus dem "Drachenbuch" von Aho/Sethi/Ullman
163 for (i
=0,n
=Key
.Len(); i
<n
; i
++)
165 h
= (h
<<4) + (ULONG
)(USHORT
)Key
.GetBuffer()[i
];
178 ULONG
HashTable::DHash(ByteString
const& Key
, ULONG lOldHash
) const
180 ULONG lHash
= lOldHash
;
183 for (i
=0,n
=Key
.Len(); i
<n
; i
++)
186 lHash
+= (ULONG
)(USHORT
)Key
.GetBuffer()[i
];
194 + (char)Key.GetStr()[0] * 256
195 + (char)Key.GetStr()[Key.Len()-1]
202 ULONG
HashTable::Probe(ULONG lPos
) const
203 // gibt den Folgewert von lPos zurück
205 lPos
++; if (lPos
==m_lSize
) lPos
=0;
209 BOOL
HashTable::IsFull() const
211 return m_lElem
>=m_lSize
;
214 BOOL
HashTable::Insert(ByteString
const& Key
, void* pObject
)
215 // pre: Key ist nicht im Dictionary enthalten, sonst return FALSE
216 // Dictionary ist nicht voll, sonst return FALSE
217 // post: pObject ist unter Key im Dictionary; m_nElem wurde erhöht
223 DBG_ERROR("HashTable::Insert() is full");
227 if (FindPos(Key
) != NULL
)
230 ULONG lPos
= Hash(Key
);
231 HashItem
*pItem
= &m_pData
[lPos
];
235 if (pItem
->IsEmpty())
237 pItem
->SetObject(Key
, pObject
);
241 m_aStatistic
.m_lSingleHash
++;
249 lPos
= DHash(Key
,lPos
);
250 pItem
= &m_pData
[lPos
];
252 if (pItem
->IsEmpty())
254 pItem
->SetObject(Key
, pObject
);
258 m_aStatistic
.m_lDoubleHash
++;
269 m_aStatistic
.m_lProbe
++;
273 pItem
= &m_pData
[lPos
];
275 while(!pItem
->IsEmpty());
277 pItem
->SetObject(Key
, pObject
);
282 HashItem
* HashTable::FindPos(ByteString
const& Key
) const
283 // sucht den Key; gibt Refrenz auf den Eintrag (gefunden)
284 // oder NULL (nicht gefunden) zurück
291 ULONG lPos
= Hash(Key
);
292 HashItem
*pItem
= &m_pData
[lPos
];
295 && pItem
->GetKey() == Key
)
302 if (pItem
->IsDeleted() || pItem
->IsUsed())
304 lPos
= DHash(Key
,lPos
);
305 pItem
= &m_pData
[lPos
];
308 && pItem
->GetKey() == Key
)
315 if (pItem
->IsDeleted() || pItem
->IsUsed())
325 pItem
= &m_pData
[lPos
];
327 bFound
= pItem
->IsUsed()
328 && pItem
->GetKey() == Key
;
330 bEnd
= !(n
<m_lSize
|| pItem
->IsFree());
332 while(!bFound
&& !bEnd
);
334 return bFound
? pItem
: NULL
;
343 void* HashTable::Find(ByteString
const& Key
) const
344 // Gibt Verweis des Objektes zurück, das unter Key abgespeichert ist,
345 // oder NULL wenn nicht vorhanden.
350 HashItem
*pItem
= FindPos(Key
);
353 && pItem
->GetKey() == Key
)
354 return pItem
->GetObject();
359 void* HashTable::Delete(ByteString
const& Key
)
360 // Löscht Objekt, das unter Key abgespeichert ist und gibt Verweis
362 // Gibt NULL zurück, wenn Key nicht vorhanden ist.
365 // post: Objekt ist nicht mehr enthalten; m_lElem dekrementiert
366 // Wenn die HashTable der Owner ist, wurde das Object gelöscht
368 HashItem
*pItem
= FindPos(Key
);
371 && pItem
->GetKey() == Key
)
373 void* pObject
= pItem
->GetObject();
376 OnDeleteObject(pObject
);
388 double HashTable::CalcLoadFactor() const
389 // prozentuale Belegung der Hashtabelle berechnen
391 return double(m_lElem
) / double(m_lSize
);
394 void HashTable::SmartGrow()
395 // Achtung: da die Objekte umkopiert werden, darf die OnDeleteObject-Methode
396 // nicht gerufen werden
398 double dLoadFactor
= CalcLoadFactor();
400 if (dLoadFactor
<= m_dMaxLoadFactor
)
401 return; // nothing to grow
403 ULONG lOldSize
= m_lSize
; // alte Daten sichern
404 HashItem
* pOldData
= m_pData
;
406 m_lSize
= ULONG (m_dGrowFactor
* m_lSize
); // neue Größe
407 m_pData
= new HashItem
[m_lSize
]; // neue Daten holen
410 // Zustand "Tabelle voll" wird in Insert abgefangen
419 m_lElem
= 0; // noch keine neuen Daten
421 // Umkopieren der Daten
423 for (ULONG i
=0; i
<lOldSize
; i
++)
425 HashItem
*pItem
= &pOldData
[i
];
428 Insert(pItem
->GetKey(),pItem
->GetObject());
434 // Iterator ---------------------------------------------------------
437 HashTableIterator::HashTableIterator(HashTable
const& aTable
)
443 void* HashTableIterator::GetFirst()
446 return FindValidObject(TRUE
/* forward */);
449 void* HashTableIterator::GetLast()
451 m_lAt
= m_aTable
.GetSize() -1;
452 return FindValidObject(FALSE
/* backward */);
455 void* HashTableIterator::GetNext()
457 if (m_lAt
+1 >= m_aTable
.GetSize())
461 return FindValidObject(TRUE
/* forward */);
464 void* HashTableIterator::GetPrev()
470 return FindValidObject(FALSE
/* backward */);
473 void* HashTableIterator::FindValidObject(BOOL bForward
)
474 // Sucht nach einem vorhandenen Objekt ab der aktuellen
477 // pre: ab inkl. m_lAt soll die Suche beginnen
478 // post: if not found then
479 // if bForward == TRUE then
480 // m_lAt == m_aTable.GetSize() -1
484 // m_lAt ist die gefundene Position
486 void *pObject
= m_aTable
.GetObjectAt(m_lAt
);
491 while (pObject
== NULL
492 && (bForward
? ((m_lAt
+1) < m_aTable
.GetSize())
500 pObject
= m_aTable
.GetObjectAt(m_lAt
);
507 DBG_ASSERT(bForward
? m_lAt
== m_aTable
.GetSize() -1 : m_lAt
== 0,
508 "HashTableIterator::FindValidObject()");