|
1 /* $Id$ */ |
|
2 |
|
3 #ifndef BLOB_HPP |
|
4 #define BLOB_HPP |
|
5 |
|
6 template <class Titem_> |
|
7 FORCEINLINE void MemCpyT(Titem_* d, const Titem_* s, int num_items = 1) |
|
8 { |
|
9 memcpy(d, s, num_items * sizeof(Titem_)); |
|
10 } |
|
11 |
|
12 |
|
13 /** Base class for simple binary blobs. |
|
14 Item is byte. |
|
15 The word 'simple' means: |
|
16 - no configurable allocator type (always made from heap) |
|
17 - no smart deallocation - deallocation must be called from the same |
|
18 module (DLL) where the blob was allocated |
|
19 - no configurable allocation policy (how big blocks should be allocated) |
|
20 - no extra ownership policy (i.e. 'copy on write') when blob is copied |
|
21 - no thread synchronization at all */ |
|
22 class CBlobBaseSimple { |
|
23 protected: |
|
24 struct CHdr { |
|
25 int m_size; // actual blob size in bytes |
|
26 int m_max_size; // maximum (allocated) size in bytes |
|
27 }; |
|
28 |
|
29 union { |
|
30 int8 *m_pData; |
|
31 CHdr *m_pHdr_1; |
|
32 } ptr_u; |
|
33 |
|
34 public: |
|
35 ST_CONST(int, Ttail_reserve = 4); // four extra bytes will be always allocated and zeroed at the end |
|
36 |
|
37 FORCEINLINE CBlobBaseSimple() { InitEmpty(); } |
|
38 FORCEINLINE CBlobBaseSimple(const CBlobBaseSimple& src) |
|
39 { |
|
40 InitEmpty(); |
|
41 AppendRaw(src); |
|
42 } |
|
43 FORCEINLINE ~CBlobBaseSimple() { Free(); } |
|
44 protected: |
|
45 FORCEINLINE void InitEmpty() { static CHdr hdrEmpty[] = {{0, 0}, {0, 0}}; ptr_u.m_pHdr_1 = &hdrEmpty[1]; } |
|
46 FORCEINLINE void Init(CHdr* hdr) { ptr_u.m_pHdr_1 = &hdr[1]; } |
|
47 FORCEINLINE CHdr& Hdr() { return ptr_u.m_pHdr_1[-1]; } |
|
48 FORCEINLINE const CHdr& Hdr() const { return ptr_u.m_pHdr_1[-1]; } |
|
49 FORCEINLINE int& RawSizeRef() { return Hdr().m_size; }; |
|
50 |
|
51 public: |
|
52 FORCEINLINE bool IsEmpty() const { return RawSize() == 0; } |
|
53 FORCEINLINE int RawSize() const { return Hdr().m_size; }; |
|
54 FORCEINLINE int MaxRawSize() const { return Hdr().m_max_size; }; |
|
55 FORCEINLINE int8* RawData() { return ptr_u.m_pData; } |
|
56 FORCEINLINE const int8* RawData() const { return ptr_u.m_pData; } |
|
57 FORCEINLINE uint32 Crc32() const {return CCrc32::Calc(RawData(), RawSize());} |
|
58 FORCEINLINE void Clear() { RawSizeRef() = 0; } |
|
59 FORCEINLINE void Free() { if (MaxRawSize() > 0) {RawFree(&Hdr()); InitEmpty();} } |
|
60 FORCEINLINE void CopyFrom(const CBlobBaseSimple& src) { Clear(); AppendRaw(src); } |
|
61 FORCEINLINE void MoveFrom(CBlobBaseSimple& src) { Free(); ptr_u.m_pData = src.ptr_u.m_pData; src.InitEmpty(); } |
|
62 FORCEINLINE void Swap(CBlobBaseSimple& src) { int8 *tmp = ptr_u.m_pData; ptr_u.m_pData = src.ptr_u.m_pData; src.ptr_u.m_pData = tmp; } |
|
63 |
|
64 FORCEINLINE void AppendRaw(int8 *p, int num_bytes) |
|
65 { |
|
66 assert(p != NULL); |
|
67 if (num_bytes > 0) { |
|
68 memcpy(GrowRawSize(num_bytes), p, num_bytes); |
|
69 } else { |
|
70 assert(num_bytes >= 0); |
|
71 } |
|
72 } |
|
73 |
|
74 FORCEINLINE void AppendRaw(const CBlobBaseSimple& src) |
|
75 { |
|
76 if (!src.IsEmpty()) |
|
77 memcpy(GrowRawSize(src.RawSize()), src.RawData(), src.RawSize()); |
|
78 } |
|
79 |
|
80 /** Reallocate if there is no free space for num_bytes bytes. |
|
81 @return pointer to the new data to be added */ |
|
82 FORCEINLINE int8* MakeRawFreeSpace(int num_bytes) |
|
83 { |
|
84 assert(num_bytes >= 0); |
|
85 int new_size = RawSize() + num_bytes; |
|
86 if (new_size > MaxRawSize()) SmartAlloc(new_size); |
|
87 FixTail(); |
|
88 return ptr_u.m_pData + RawSize(); |
|
89 } |
|
90 |
|
91 /** Increase RawSize() by num_bytes. |
|
92 @return pointer to the new data added */ |
|
93 FORCEINLINE int8* GrowRawSize(int num_bytes) |
|
94 { |
|
95 int8* pNewData = MakeRawFreeSpace(num_bytes); |
|
96 RawSizeRef() += num_bytes; |
|
97 return pNewData; |
|
98 } |
|
99 |
|
100 /** Decrease RawSize() by num_bytes. */ |
|
101 FORCEINLINE void ReduceRawSize(int num_bytes) |
|
102 { |
|
103 if (MaxRawSize() > 0 && num_bytes > 0) { |
|
104 assert(num_bytes <= RawSize()); |
|
105 if (num_bytes < RawSize()) RawSizeRef() -= num_bytes; |
|
106 else RawSizeRef() = 0; |
|
107 } |
|
108 } |
|
109 /** reallocate blob data if needed */ |
|
110 void SmartAlloc(int new_size) |
|
111 { |
|
112 int old_max_size = MaxRawSize(); |
|
113 if (old_max_size >= new_size) return; |
|
114 // calculate minimum block size we need to allocate |
|
115 int min_alloc_size = sizeof(CHdr) + new_size + Ttail_reserve; |
|
116 // ask allocation policy for some reasonable block size |
|
117 int alloc_size = AllocPolicy(min_alloc_size); |
|
118 // allocate new block |
|
119 CHdr* pNewHdr = RawAlloc(alloc_size); |
|
120 // setup header |
|
121 pNewHdr->m_size = RawSize(); |
|
122 pNewHdr->m_max_size = alloc_size - (sizeof(CHdr) + Ttail_reserve); |
|
123 // copy existing data |
|
124 if (RawSize() > 0) |
|
125 memcpy(pNewHdr + 1, ptr_u.m_pData, pNewHdr->m_size); |
|
126 // replace our block with new one |
|
127 CHdr* pOldHdr = &Hdr(); |
|
128 Init(pNewHdr); |
|
129 if (old_max_size > 0) |
|
130 RawFree(pOldHdr); |
|
131 } |
|
132 /** simple allocation policy - can be optimized later */ |
|
133 FORCEINLINE static int AllocPolicy(int min_alloc) |
|
134 { |
|
135 if (min_alloc < (1 << 9)) { |
|
136 if (min_alloc < (1 << 5)) return (1 << 5); |
|
137 return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9); |
|
138 } |
|
139 if (min_alloc < (1 << 15)) { |
|
140 if (min_alloc < (1 << 11)) return (1 << 11); |
|
141 return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15); |
|
142 } |
|
143 if (min_alloc < (1 << 20)) { |
|
144 if (min_alloc < (1 << 17)) return (1 << 17); |
|
145 return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20); |
|
146 } |
|
147 min_alloc = (min_alloc | ((1 << 20) - 1)) + 1; |
|
148 return min_alloc; |
|
149 } |
|
150 |
|
151 /** all allocation should happen here */ |
|
152 static FORCEINLINE CHdr* RawAlloc(int num_bytes) { return (CHdr*)malloc(num_bytes); } |
|
153 /** all deallocations should happen here */ |
|
154 static FORCEINLINE void RawFree(CHdr* p) { free(p); } |
|
155 /** fixing the four bytes at the end of blob data - useful when blob is used to hold string */ |
|
156 FORCEINLINE void FixTail() |
|
157 { |
|
158 if (MaxRawSize() > 0) { |
|
159 int8 *p = &ptr_u.m_pData[RawSize()]; |
|
160 for (int i = 0; i < Ttail_reserve; i++) p[i] = 0; |
|
161 } |
|
162 } |
|
163 }; |
|
164 |
|
165 template <class Titem_, class Tbase_ = CBlobBaseSimple> |
|
166 class CBlobT : public CBlobBaseSimple { |
|
167 // make template arguments public: |
|
168 public: |
|
169 typedef Titem_ Titem; |
|
170 typedef Tbase_ Tbase; |
|
171 |
|
172 ST_CONST(int, Titem_size = sizeof(Titem)); |
|
173 |
|
174 FORCEINLINE CBlobT() : Tbase() {} |
|
175 FORCEINLINE CBlobT(const Tbase& src) : Tbase(src) {assert((RawSize() % Titem_size) == 0);} |
|
176 FORCEINLINE ~CBlobT() { Free(); } |
|
177 FORCEINLINE void CheckIdx(int idx) { assert(idx >= 0); assert(idx < Size()); } |
|
178 FORCEINLINE Titem* Data() { return (Titem*)RawData(); } |
|
179 FORCEINLINE const Titem* Data() const { return (const Titem*)RawData(); } |
|
180 FORCEINLINE Titem* Data(int idx) { CheckIdx(idx); return (Data() + idx); } |
|
181 FORCEINLINE const Titem* Data(int idx) const { CheckIdx(idx); return (Data() + idx); } |
|
182 FORCEINLINE int Size() const { return (RawSize() / Titem_size); } |
|
183 FORCEINLINE void Free() |
|
184 { |
|
185 assert((RawSize() % Titem_size) == 0); |
|
186 int old_size = Size(); |
|
187 if (old_size > 0) { |
|
188 // destroy removed items; |
|
189 Titem* pI_last_to_destroy = Data(0); |
|
190 for (Titem* pI = Data(old_size - 1); pI >= pI_last_to_destroy; pI--) pI->~Titem_(); |
|
191 } |
|
192 Tbase::Free(); |
|
193 } |
|
194 FORCEINLINE Titem* GrowSizeNC(int num_items) { return (Titem*)GrowRawSize(num_items * Titem_size); } |
|
195 FORCEINLINE Titem* GrowSizeC(int num_items) |
|
196 { |
|
197 Titem* pI = GrowSizeNC(num_items); |
|
198 for (int i = num_items; i > 0; i--, pI++) new (pI) Titem(); |
|
199 } |
|
200 FORCEINLINE void ReduceSize(int num_items) |
|
201 { |
|
202 assert((RawSize() % Titem_size) == 0); |
|
203 int old_size = Size(); |
|
204 assert(num_items <= old_size); |
|
205 int new_size = (num_items <= old_size) ? (old_size - num_items) : 0; |
|
206 // destroy removed items; |
|
207 Titem* pI_last_to_destroy = Data(new_size); |
|
208 for (Titem* pI = Data(old_size - 1); pI >= pI_last_to_destroy; pI--) pI->~Titem(); |
|
209 // remove them |
|
210 ReduceRawSize(num_items * Titem_size); |
|
211 } |
|
212 FORCEINLINE Titem* AppendNew() |
|
213 { |
|
214 Titem& dst = *GrowSizeNC(1); |
|
215 Titem* pNewItem = new (&dst) Titem(); |
|
216 return pNewItem; |
|
217 } |
|
218 FORCEINLINE Titem* Append(const Titem& src) |
|
219 { |
|
220 Titem& dst = *GrowSizeNC(1); |
|
221 Titem* pNewItem = new (&dst) Titem(src); |
|
222 return pNewItem; |
|
223 } |
|
224 FORCEINLINE Titem* Append(const Titem* pSrc, int num_items) |
|
225 { |
|
226 Titem* pDst = GrowSizeNC(num_items); |
|
227 Titem* pDstOrg = pDst; |
|
228 Titem* pDstEnd = pDst + num_items; |
|
229 while (pDst < pDstEnd) new (pDst++) Titem(*(pSrc++)); |
|
230 return pDstOrg; |
|
231 } |
|
232 FORCEINLINE void RemoveBySwap(int idx) |
|
233 { |
|
234 CheckIdx(idx); |
|
235 // destroy removed item |
|
236 Titem* pRemoved = Data(idx); |
|
237 RemoveBySwap(pRemoved); |
|
238 } |
|
239 FORCEINLINE void RemoveBySwap(Titem* pItem) |
|
240 { |
|
241 Titem* pLast = Data(Size() - 1); |
|
242 assert(pItem >= Data() && pItem <= pLast); |
|
243 // move last item to its new place |
|
244 if (pItem != pLast) { |
|
245 pItem->~Titem_(); |
|
246 new (pItem) Titem_(*pLast); |
|
247 } |
|
248 // destroy the last item |
|
249 pLast->~Titem_(); |
|
250 // and reduce the raw blob size |
|
251 ReduceRawSize(Titem_size); |
|
252 } |
|
253 FORCEINLINE Titem* MakeFreeSpace(int num_items) { return (Titem*)MakeRawFreeSpace(num_items * Titem_size); } |
|
254 }; |
|
255 |
|
256 // simple string implementation |
|
257 struct CStrA : public CBlobT<char> |
|
258 { |
|
259 typedef CBlobT<char> base; |
|
260 CStrA(const char* str = NULL) {Append(str);} |
|
261 FORCEINLINE CStrA(const CBlobBaseSimple& src) : base(src) {} |
|
262 void Append(const char* str) {if (str != NULL && str[0] != '\0') base::Append(str, (int)strlen(str));} |
|
263 }; |
|
264 |
|
265 #endif /* BLOB_HPP */ |
|
266 |