blob: bae9ed4594e3b3a920d4bc626fe805bb419d0d19 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
junov@google.comf93e7172011-03-31 21:26:24 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
junov@google.comf93e7172011-03-31 21:26:24 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
junov@google.comf93e7172011-03-31 21:26:24 +000010#ifndef GrBinHashKey_DEFINED
11#define GrBinHashKey_DEFINED
12
13#include "GrTypes.h"
14
15/**
16 * Abstract base class that presents the building interface of GrBinHashKey.
17 * This base class allows builder methods to not know the exact template
18 * parameters of GrBinHashKey
19 */
20class GrBinHashKeyBuilder {
21public:
22 GrBinHashKeyBuilder() {}
23 virtual ~GrBinHashKeyBuilder() {}
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000024 virtual void keyData(const uint32_t *dataToAdd, size_t len) = 0;
junov@google.comf93e7172011-03-31 21:26:24 +000025};
26
27/**
28 * Hash function class than can take a data stream of indeterminate length.
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000029 * It also has the ability to recieve data in several chunks (steamed). The
30 * hash function used is the One-at-a-Time Hash
31 * (http://burtleburtle.net/bob/hash/doobs.html).
junov@google.comf93e7172011-03-31 21:26:24 +000032 *
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000033 * Keys are built in two passes the first pass builds the key until the
junov@google.comf93e7172011-03-31 21:26:24 +000034 * allocated storage for the key runs out, raw data accumulation stops, but
35 * the calculation of the 32-bit hash value and total key length continue.
36 * The second pass is only necessary if storage ran-out during the first pass.
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000037 * If that is the case, the heap storage portion of the key will be
junov@google.comf93e7172011-03-31 21:26:24 +000038 * re-allocated so that the entire key can be stored in the second pass.
39 *
40 * Code for building a key:
41 *
42 * GrBinHashKey<MyEntryStruct, MyStackSize> MyKey;
43 * while( MyKey->doPass() )
44 * {
45 * MyObject->buildKey(&MyKey); //invoke a builder method
46 * }
47 *
48 * All the builder method needs to do is make calls to the keyData method to
49 * append binary data to the key.
50 */
51template<typename Entry, size_t StackSize>
52class GrBinHashKey : public GrBinHashKeyBuilder {
53public:
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000054 GrBinHashKey()
55 : fA(0)
junov@google.comf93e7172011-03-31 21:26:24 +000056 , fLength(0)
57 , fHeapData(NULL)
58 , fPhysicalSize(StackSize)
59 , fUseHeap(false)
60 , fPass(0)
61#if GR_DEBUG
62 , fIsValid(true)
63#endif
64 {}
65
66private:
67 // Illegal: must choose explicitly between copyAndTakeOwnership
68 // and deepCopyFrom.
69 // Not inheriting GrNoncopyable, because it causes very obscure compiler
70 // errors with template classes, which are hard to trace back to the use
71 // of assignment.
72 GrBinHashKey(const GrBinHashKey<Entry, StackSize>&) {}
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000073 GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry,
junov@google.comf93e7172011-03-31 21:26:24 +000074 StackSize>&) {
75 return this;
76 }
77
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +000078public:
junov@google.comf93e7172011-03-31 21:26:24 +000079 void copyAndTakeOwnership(GrBinHashKey<Entry, StackSize>& key) {
junov@google.comf93e7172011-03-31 21:26:24 +000080 GrAssert(key.fIsValid);
junov@google.com5d6e1082011-05-25 20:26:11 +000081 copyFields(key);
junov@google.comf93e7172011-03-31 21:26:24 +000082 if (fUseHeap) {
83 key.fHeapData = NULL; // ownership transfer
84 }
85 // Consistency Checking
86 // To avoid the overhead of copying or ref-counting the dynamically
87 // allocated portion of the key, we use ownership transfer
88 // Key usability is only tracked in debug builds.
89 GR_DEBUGCODE(key.fIsValid = false;)
90 }
91
92 void deepCopyFrom(const GrBinHashKey<Entry, StackSize>& key) {
93 GrAssert(key.fIsValid);
junov@google.com5d6e1082011-05-25 20:26:11 +000094 copyFields(key);
junov@google.comf93e7172011-03-31 21:26:24 +000095 if (fUseHeap) {
96 fHeapData = reinterpret_cast<uint8_t*>(
97 GrMalloc(sizeof(uint8_t) * fPhysicalSize));
98 memcpy(fHeapData, key.fHeapData, fLength);
99 }
100 }
101
102 virtual ~GrBinHashKey() {
103 if (fUseHeap) {
104 GrFree(fHeapData);
105 }
106 }
107
108 bool doPass() {
109 GrAssert(fIsValid);
110 if (0 == fPass) {
111 fPass++;
112 return true;
113 }
114 if (1 == fPass) {
115 bool passNeeded = false;
116 if (fLength > fPhysicalSize) {
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000117 // If the first pass ran out of space the we need to
junov@google.comf93e7172011-03-31 21:26:24 +0000118 // re-allocate and perform a second pass
119 GrFree(fHeapData);
120 fHeapData = reinterpret_cast<uint8_t*>(
121 GrMalloc(sizeof(uint8_t) * fLength));
122 fPhysicalSize = fLength;
123 fUseHeap = true;
124 passNeeded = true;
125 fLength = 0;
126 }
127 fPass++;
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000128 return passNeeded;
junov@google.comf93e7172011-03-31 21:26:24 +0000129 }
130 return false;
131 }
132
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000133 void keyData(const uint32_t *dataToAdd, size_t len) {
junov@google.comf93e7172011-03-31 21:26:24 +0000134 GrAssert(fIsValid);
135 GrAssert(fPass);
tomhudson@google.com78e7d2c2011-06-01 20:43:05 +0000136 GrAssert(GrIsALIGN4(len));
junov@google.comf93e7172011-03-31 21:26:24 +0000137 if (fUseHeap) {
138 GrAssert(fHeapData);
139 GrAssert(fLength + len <= fPhysicalSize);
140 memcpy(&fHeapData[fLength], dataToAdd, len );
141 } else {
142 if (fLength + len <= StackSize) {
143 memcpy(&fStackData[fLength], dataToAdd, len);
144 } else {
145 GrAssert(1 == fPass);
146 }
147 }
148
149 fLength += len;
150
151 if (1 == fPass) {
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000152 uint32_t a = fA;
153 while (len >= 4) {
154 a += *dataToAdd++;
155 a += (a << 10);
156 a ^= (a >> 6);
157 len -= 4;
junov@google.comf93e7172011-03-31 21:26:24 +0000158 }
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000159 a += (a << 3);
160 a ^= (a >> 11);
161 a += (a << 15);
162
163 fA = a;
junov@google.comf93e7172011-03-31 21:26:24 +0000164 }
165 }
166
167 int compare(const GrBinHashKey<Entry, StackSize>& key) const {
168 GrAssert(fIsValid);
169 if (fLength == key.fLength) {
170 GrAssert(fUseHeap == key.fUseHeap);
171 if(fUseHeap) {
172 return memcmp(fHeapData, key.fHeapData, fLength);
173 } else {
174 return memcmp(fStackData, key.fStackData, fLength);
175 }
176 }
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000177
junov@google.comf93e7172011-03-31 21:26:24 +0000178 return (fLength - key.fLength);
179 }
180
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000181 static bool
junov@google.comf93e7172011-03-31 21:26:24 +0000182 EQ(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) {
183 GrAssert(key.fIsValid);
184 return 0 == entry.compare(key);
185 }
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000186
187 static bool
junov@google.comf93e7172011-03-31 21:26:24 +0000188 LT(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) {
189 GrAssert(key.fIsValid);
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000190 return entry.compare(key) < 0;
junov@google.comf93e7172011-03-31 21:26:24 +0000191 }
192
193 uint32_t getHash() const {
194 GrAssert(fIsValid);
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000195 return fA;
junov@google.comf93e7172011-03-31 21:26:24 +0000196 }
197
198private:
junov@google.com5d6e1082011-05-25 20:26:11 +0000199 void copyFields(const GrBinHashKey<Entry, StackSize>& src) {
200 if (fUseHeap) {
201 GrFree(fHeapData);
202 }
203 // We do a field-by-field copy because this is a non-POD
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000204 // class, and therefore memcpy would be bad
junov@google.com5d6e1082011-05-25 20:26:11 +0000205 fA = src.fA;
junov@google.com5d6e1082011-05-25 20:26:11 +0000206 fLength = src.fLength;
207 memcpy(fStackData, src.fStackData, StackSize);
208 fHeapData = src.fHeapData;
209 fPhysicalSize = src.fPhysicalSize;
210 fUseHeap = src.fUseHeap;
211 fPass = src.fPass;
212 }
213
junov@google.comf93e7172011-03-31 21:26:24 +0000214 uint32_t fA;
junov@google.comf93e7172011-03-31 21:26:24 +0000215
216 // For accumulating the variable length binary key
217 size_t fLength; // length of data accumulated so far
218 uint8_t fStackData[StackSize]; //Buffer for key storage
219 uint8_t* fHeapData; //Dynamically allocated extended key storage
220 size_t fPhysicalSize; //Total size available for key storage
221 bool fUseHeap; //Using a dynamically allocated key storage
222 int fPass; //Key generation pass counter
223
224#if GR_DEBUG
225public:
226 bool fIsValid;
227#endif
228};
229
230#endif