1 //
2 // $Id$
3 //
4 
5 //
6 // Copyright (c) 2001-2016, Andrew Aksyonoff
7 // Copyright (c) 2008-2016, Sphinx Technologies Inc
8 // All rights reserved
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License. You should have
12 // received a copy of the GPL license along with this program; if you
13 // did not, you can find it at http://www.gnu.org/
14 //
15 
16 #include "sphinx.h"
17 #include "sphinxint.h"
18 #include "sphinxutils.h"
19 
20 #if !USE_WINDOWS
21 #include <sys/time.h> // for gettimeofday
22 
23 // define this if you want to run gprof over the threads model - to track children threads also.
24 #define USE_GPROF 0
25 
26 #endif
27 
28 int g_iThreadStackSize = 1024*1024;
29 
30 //////////////////////////////////////////////////////////////////////////
31 
32 char CSphString::EMPTY[] = "";
33 
34 #if USE_WINDOWS
35 #ifndef NDEBUG
36 
sphAssert(const char * sExpr,const char * sFile,int iLine)37 void sphAssert ( const char * sExpr, const char * sFile, int iLine )
38 {
39 	char sBuffer [ 1024 ];
40 	_snprintf ( sBuffer, sizeof(sBuffer), "%s(%d): assertion %s failed\n", sFile, iLine, sExpr );
41 
42 	if ( MessageBox ( NULL, sBuffer, "Assert failed! Cancel to debug.",
43 		MB_OKCANCEL | MB_TOPMOST | MB_SYSTEMMODAL | MB_ICONEXCLAMATION )!=IDOK )
44 	{
45 		__debugbreak ();
46 	} else
47 	{
48 		fprintf ( stdout, "%s", sBuffer );
49 		exit ( 1 );
50 	}
51 }
52 
53 #endif // !NDEBUG
54 #endif // USE_WINDOWS
55 
56 /////////////////////////////////////////////////////////////////////////////
57 // DEBUG MEMORY MANAGER
58 /////////////////////////////////////////////////////////////////////////////
59 
60 #if SPH_DEBUG_LEAKS
61 
62 #undef new
63 #define SPH_DEBUG_DOFREE 1 // 0 will not actually free returned blocks; helps to catch double deletes etc
64 
65 const DWORD MEMORY_MAGIC_PLAIN		= 0xbbbbbbbbUL;
66 const DWORD MEMORY_MAGIC_ARRAY		= 0xaaaaaaaaUL;
67 const DWORD MEMORY_MAGIC_END		= 0xeeeeeeeeUL;
68 const DWORD MEMORY_MAGIC_DELETED	= 0xdedededeUL;
69 
70 
71 struct CSphMemHeader
72 {
73 	DWORD			m_uMagic;
74 	const char *	m_sFile;
75 #if SPH_DEBUG_BACKTRACES
76 	const char *	m_sBacktrace;
77 #endif
78 	int				m_iLine;
79 	size_t			m_iSize;
80 	int				m_iAllocId;
81 	BYTE *			m_pPointer;
82 	CSphMemHeader *	m_pNext;
83 	CSphMemHeader *	m_pPrev;
84 };
85 
86 static CSphStaticMutex	g_tAllocsMutex;
87 
88 static int				g_iCurAllocs	= 0;
89 static int				g_iAllocsId		= 0;
90 static CSphMemHeader *	g_pAllocs		= NULL;
91 static int64_t			g_iCurBytes		= 0;
92 static int				g_iTotalAllocs	= 0;
93 static int				g_iPeakAllocs	= 0;
94 static int64_t			g_iPeakBytes	= 0;
95 #if SPH_ALLOC_FILL
96 static bool				g_bFirstRandomAlloc = true;
97 #endif
98 
sphDebugNew(size_t iSize,const char * sFile,int iLine,bool bArray)99 void * sphDebugNew ( size_t iSize, const char * sFile, int iLine, bool bArray )
100 {
101 	BYTE * pBlock = (BYTE*) ::malloc ( iSize+sizeof(CSphMemHeader)+sizeof(DWORD) );
102 	if ( !pBlock )
103 		sphDie ( "out of memory (unable to allocate " UINT64_FMT " bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
104 
105 	*(DWORD*)( pBlock+iSize+sizeof(CSphMemHeader) ) = MEMORY_MAGIC_END;
106 	g_tAllocsMutex.Lock();
107 
108 	CSphMemHeader * pHeader = (CSphMemHeader*) pBlock;
109 	pHeader->m_uMagic = bArray ? MEMORY_MAGIC_ARRAY : MEMORY_MAGIC_PLAIN;
110 	pHeader->m_sFile = sFile;
111 #if SPH_ALLOC_FILL
112 	if ( g_bFirstRandomAlloc )
113 	{
114 		sphAutoSrand();
115 		g_bFirstRandomAlloc = false;
116 	}
117 
118 	BYTE * pBlockPtr = (BYTE*)(pHeader+1);
119 	for ( size_t i = 0; i < iSize; i++ )
120 		*pBlockPtr++ = BYTE(sphRand () & 0xFF);
121 #endif
122 #if SPH_DEBUG_BACKTRACES
123 	const char * sTrace = DoBacktrace ( 0, 3 );
124 	if ( sTrace )
125 	{
126 		char * pTrace = (char*) ::malloc ( strlen(sTrace) + 1 );
127 		strcpy ( pTrace, sTrace ); //NOLINT
128 		pHeader->m_sBacktrace = pTrace;
129 	} else
130 		pHeader->m_sBacktrace = NULL;
131 #endif
132 	pHeader->m_iLine = iLine;
133 	pHeader->m_iSize = iSize;
134 	pHeader->m_iAllocId = ++g_iAllocsId;
135 	pHeader->m_pPointer = pBlock;
136 	pHeader->m_pNext = g_pAllocs;
137 	pHeader->m_pPrev = NULL;
138 	if ( g_pAllocs )
139 	{
140 		assert ( !g_pAllocs->m_pPrev );
141 		g_pAllocs->m_pPrev = pHeader;
142 	}
143 	g_pAllocs = pHeader;
144 
145 	g_iCurAllocs++;
146 	g_iCurBytes += iSize;
147 
148 	g_iTotalAllocs++;
149 	g_iPeakAllocs = Max ( g_iPeakAllocs, g_iCurAllocs );
150 	g_iPeakBytes = Max ( g_iPeakBytes, g_iCurBytes );
151 
152 	g_tAllocsMutex.Unlock();
153 	return pHeader+1;
154 }
155 
156 
sphDebugDelete(void * pPtr,bool bArray)157 void sphDebugDelete ( void * pPtr, bool bArray )
158 {
159 	if ( !pPtr )
160 		return;
161 	g_tAllocsMutex.Lock();
162 
163 	CSphMemHeader * pHeader = ((CSphMemHeader*)pPtr)-1;
164 	switch ( pHeader->m_uMagic )
165 	{
166 		case MEMORY_MAGIC_ARRAY:
167 			if ( !bArray )
168 				sphDie ( "delete [] on non-array block %d allocated at %s(%d)",
169 					pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
170 			break;
171 
172 		case MEMORY_MAGIC_PLAIN:
173 			if ( bArray )
174 				sphDie ( "delete on array block %d allocated at %s(%d)",
175 					pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
176 			break;
177 
178 		case MEMORY_MAGIC_DELETED:
179 			sphDie ( "double delete on block %d allocated at %s(%d)",
180 				pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
181 			break;
182 
183 		default:
184 			sphDie ( "delete on unmanaged block at 0x%08x", pPtr );
185 			return;
186 	}
187 
188 	BYTE * pBlock = (BYTE*) pHeader;
189 	if ( *(DWORD*)( pBlock+pHeader->m_iSize+sizeof(CSphMemHeader) )!=MEMORY_MAGIC_END )
190 		sphDie ( "out-of-bounds write beyond block %d allocated at %s(%d)",
191 			pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
192 
193 	// unchain
194 	if ( pHeader==g_pAllocs )
195 		g_pAllocs = g_pAllocs->m_pNext;
196 
197 	if ( pHeader->m_pPrev )
198 	{
199 		assert ( pHeader->m_pPrev->m_uMagic==MEMORY_MAGIC_PLAIN || pHeader->m_pPrev->m_uMagic==MEMORY_MAGIC_ARRAY );
200 		pHeader->m_pPrev->m_pNext = pHeader->m_pNext;
201 	}
202 	if ( pHeader->m_pNext )
203 	{
204 		assert ( pHeader->m_pNext->m_uMagic==MEMORY_MAGIC_PLAIN || pHeader->m_pNext->m_uMagic==MEMORY_MAGIC_ARRAY );
205 		pHeader->m_pNext->m_pPrev = pHeader->m_pPrev;
206 	}
207 
208 	pHeader->m_pPrev = NULL;
209 	pHeader->m_pNext = NULL;
210 
211 	// mark and delete
212 	pHeader->m_uMagic = MEMORY_MAGIC_DELETED;
213 
214 	g_iCurAllocs--;
215 	g_iCurBytes -= pHeader->m_iSize;
216 
217 #if SPH_DEBUG_BACKTRACES
218 	if ( pHeader->m_sBacktrace )
219 		::free ( (void*) pHeader->m_sBacktrace );
220 #endif
221 
222 #if SPH_DEBUG_DOFREE
223 	::free ( pHeader );
224 #endif
225 
226 	g_tAllocsMutex.Unlock();
227 }
228 
229 
sphAllocBytes()230 int64_t	sphAllocBytes ()
231 {
232 	return g_iCurBytes;
233 }
234 
235 
sphAllocsCount()236 int sphAllocsCount ()
237 {
238 	return g_iCurAllocs;
239 }
240 
241 
sphAllocsLastID()242 int sphAllocsLastID ()
243 {
244 	return g_iAllocsId;
245 }
246 
247 
sphAllocsDump(int iFile,int iSinceID)248 void sphAllocsDump ( int iFile, int iSinceID )
249 {
250 	g_tAllocsMutex.Lock();
251 
252 	sphSafeInfo ( iFile, "--- dumping allocs since %d ---\n", iSinceID );
253 
254 	uint64_t iTotalBytes = 0;
255 	int iTotal = 0;
256 
257 	for ( CSphMemHeader * pHeader = g_pAllocs;
258 		pHeader && pHeader->m_iAllocId > iSinceID;
259 		pHeader = pHeader->m_pNext )
260 	{
261 		sphSafeInfo ( iFile, "alloc %d at %s(%d): 0x%0p %d bytes\n", pHeader->m_iAllocId,
262 			pHeader->m_sFile, pHeader->m_iLine, pHeader->m_pPointer, (int)pHeader->m_iSize );
263 
264 #if SPH_DEBUG_BACKTRACES
265 		sphSafeInfo ( iFile, "Backtrace:\n%s\n", pHeader->m_sBacktrace );
266 #endif
267 
268 		iTotalBytes += pHeader->m_iSize;
269 		iTotal++;
270 	}
271 
272 	sphSafeInfo ( iFile, "total allocs %d: %d.%03d bytes", iTotal, (int)(iTotalBytes/1024), (int)(iTotalBytes%1000) );
273 	sphSafeInfo ( iFile, "--- end of dump ---\n" );
274 
275 	g_tAllocsMutex.Unlock();
276 }
277 
278 
sphAllocsStats()279 void sphAllocsStats ()
280 {
281 	fprintf ( stdout, "--- total-allocs=%d, peak-allocs=%d, peak-bytes=" INT64_FMT "\n",
282 		g_iTotalAllocs, g_iPeakAllocs, g_iPeakBytes );
283 }
284 
285 
sphAllocsCheck()286 void sphAllocsCheck ()
287 {
288 	g_tAllocsMutex.Lock();
289 	for ( CSphMemHeader * pHeader=g_pAllocs; pHeader; pHeader=pHeader->m_pNext )
290 	{
291 		BYTE * pBlock = (BYTE*) pHeader;
292 
293 		if (!( pHeader->m_uMagic==MEMORY_MAGIC_ARRAY || pHeader->m_uMagic==MEMORY_MAGIC_PLAIN ))
294 			sphDie ( "corrupted header in block %d allocated at %s(%d)",
295 				pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
296 
297 		if ( *(DWORD*)( pBlock+pHeader->m_iSize+sizeof(CSphMemHeader) )!=MEMORY_MAGIC_END )
298 			sphDie ( "out-of-bounds write beyond block %d allocated at %s(%d)",
299 				pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
300 	}
301 	g_tAllocsMutex.Unlock();
302 }
303 
sphMemStatInit()304 void sphMemStatInit () {}
sphMemStatDone()305 void sphMemStatDone () {}
sphMemStatDump(int)306 void sphMemStatDump ( int ) {}
307 
308 //////////////////////////////////////////////////////////////////////////
309 
operator new(size_t iSize,const char * sFile,int iLine)310 void * operator new ( size_t iSize, const char * sFile, int iLine )
311 {
312 	return sphDebugNew ( iSize, sFile, iLine, false );
313 }
314 
315 
operator new[](size_t iSize,const char * sFile,int iLine)316 void * operator new [] ( size_t iSize, const char * sFile, int iLine )
317 {
318 	return sphDebugNew ( iSize, sFile, iLine, true );
319 }
320 
321 
operator delete(void * pPtr)322 void operator delete ( void * pPtr )
323 {
324 	sphDebugDelete ( pPtr, false );
325 }
326 
327 
operator delete[](void * pPtr)328 void operator delete [] ( void * pPtr )
329 {
330 	sphDebugDelete ( pPtr, true );
331 }
332 
333 //////////////////////////////////////////////////////////////////////////////
334 // ALLOCACTIONS COUNT/SIZE PROFILER
335 //////////////////////////////////////////////////////////////////////////////
336 
337 #else
338 #if SPH_ALLOCS_PROFILER
339 
340 #undef new
341 
342 static CSphStaticMutex	g_tAllocsMutex;
343 static int				g_iAllocsId		= 0;
344 static int				g_iCurAllocs	= 0;
345 static int64_t			g_iCurBytes		= 0;
346 static int				g_iTotalAllocs	= 0;
347 static int				g_iPeakAllocs	= 0;
348 static int64_t			g_iPeakBytes	= 0;
349 
350 // statictic's per memory category
351 struct MemCategorized_t
352 {
353 	int64_t	m_iSize;
354 	int		m_iCount;
355 
MemCategorized_tMemCategorized_t356 	MemCategorized_t()
357 		: m_iSize ( 0 )
358 		, m_iCount ( 0 )
359 	{
360 	}
361 };
362 
363 static MemCategory_e sphMemStatGet ();
364 
365 // memory categories storage
366 static MemCategorized_t g_dMemCategoryStat [ MEM_TOTAL ];
367 
368 //////////////////////////////////////////////////////////////////////////
369 // ALLOCATIONS COUNT/SIZE PROFILER
370 //////////////////////////////////////////////////////////////////////////
371 
sphDebugNew(size_t iSize)372 void * sphDebugNew ( size_t iSize )
373 {
374 	BYTE * pBlock = (BYTE*) ::malloc ( iSize+sizeof(size_t)*2 );
375 	if ( !pBlock )
376 		sphDie ( "out of memory (unable to allocate %"PRIu64" bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
377 
378 	const int iMemType = sphMemStatGet();
379 	assert ( iMemType>=0 && iMemType<MEM_TOTAL );
380 
381 	g_tAllocsMutex.Lock ();
382 
383 	g_iAllocsId++;
384 	g_iCurAllocs++;
385 	g_iCurBytes += iSize;
386 	g_iTotalAllocs++;
387 	g_iPeakAllocs = Max ( g_iCurAllocs, g_iPeakAllocs );
388 	g_iPeakBytes = Max ( g_iCurBytes, g_iPeakBytes );
389 
390 	g_dMemCategoryStat[iMemType].m_iSize += iSize;
391 	g_dMemCategoryStat[iMemType].m_iCount++;
392 
393 	g_tAllocsMutex.Unlock ();
394 
395 	size_t * pData = (size_t *)pBlock;
396 	pData[0] = iSize;
397 	pData[1] = iMemType;
398 
399 	return pBlock + sizeof(size_t)*2;
400 }
401 
sphDebugDelete(void * pPtr)402 void sphDebugDelete ( void * pPtr )
403 {
404 	if ( !pPtr )
405 		return;
406 
407 	size_t * pBlock = (size_t*) pPtr;
408 	pBlock -= 2;
409 
410 	const int iSize = pBlock[0];
411 	const int iMemType = pBlock[1];
412 	assert ( iMemType>=0 && iMemType<MEM_TOTAL );
413 
414 	g_tAllocsMutex.Lock ();
415 
416 	g_iCurAllocs--;
417 	g_iCurBytes -= iSize;
418 
419 	g_dMemCategoryStat[iMemType].m_iSize -= iSize;
420 	g_dMemCategoryStat[iMemType].m_iCount--;
421 
422 	g_tAllocsMutex.Unlock ();
423 
424 	::free ( pBlock );
425 }
426 
sphAllocsStats()427 void sphAllocsStats ()
428 {
429 	g_tAllocsMutex.Lock ();
430 	fprintf ( stdout, "--- total-allocs=%d, peak-allocs=%d, peak-bytes=" INT64_FMT "\n",
431 		g_iTotalAllocs, g_iPeakAllocs, g_iPeakBytes );
432 	g_tAllocsMutex.Unlock ();
433 }
434 
sphAllocBytes()435 int64_t sphAllocBytes ()		{ return g_iCurBytes; }
sphAllocsCount()436 int sphAllocsCount ()			{ return g_iCurAllocs; }
sphAllocsLastID()437 int sphAllocsLastID ()			{ return g_iAllocsId; }
sphAllocsDump(int,int)438 void sphAllocsDump ( int, int )	{}
sphAllocsCheck()439 void sphAllocsCheck ()			{}
440 
operator new(size_t iSize,const char *,int)441 void * operator new ( size_t iSize, const char *, int )		{ return sphDebugNew ( iSize ); }
operator new[](size_t iSize,const char *,int)442 void * operator new [] ( size_t iSize, const char *, int )	{ return sphDebugNew ( iSize ); }
operator delete(void * pPtr)443 void operator delete ( void * pPtr )						{ sphDebugDelete ( pPtr ); }
operator delete[](void * pPtr)444 void operator delete [] ( void * pPtr )						{ sphDebugDelete ( pPtr ); }
445 
446 
447 //////////////////////////////////////////////////////////////////////////////
448 // MEMORY STATISTICS
449 //////////////////////////////////////////////////////////////////////////////
450 
451 /// TLS key of memory category stack
452 SphThreadKey_t g_tTLSMemCategory;
453 
454 STATIC_ASSERT ( MEM_TOTAL<255, TOO_MANY_MEMORY_CATEGORIES );
455 
456 // stack of memory categories as we move deeper and deeper
457 class MemCategoryStack_t // NOLINT
458 {
459 #define MEM_STACK_MAX 1024
460 	BYTE m_dStack[MEM_STACK_MAX];
461 	int m_iDepth;
462 
463 public:
464 
465 	// ctor ( cross platform )
Reset()466 	void Reset ()
467 	{
468 		m_iDepth = 0;
469 		m_dStack[0] = MEM_CORE;
470 	}
471 
Push(MemCategory_e eCategory)472 	void Push ( MemCategory_e eCategory )
473 	{
474 		assert ( eCategory>=0 && eCategory<MEM_TOTAL );
475 		assert ( m_iDepth+1<MEM_STACK_MAX );
476 		m_dStack[++m_iDepth] = (BYTE)eCategory;
477 	}
478 
479 #ifndef NDEBUG
Pop(MemCategory_e eCategory)480 	void Pop ( MemCategory_e eCategory )
481 	{
482 		assert ( eCategory>=0 && eCategory<MEM_TOTAL );
483 #else
484 	void Pop ( MemCategory_e )
485 	{
486 #endif
487 
488 		assert ( m_iDepth-1>=0 );
489 		assert ( m_dStack[m_iDepth]==eCategory );
490 		m_iDepth--;
491 	}
492 
493 	MemCategory_e Top () const
494 	{
495 		assert ( m_iDepth>= 0 && m_iDepth<MEM_STACK_MAX );
496 		assert ( m_dStack[m_iDepth]>=0 && m_dStack[m_iDepth]<MEM_TOTAL );
497 		return MemCategory_e ( m_dStack[m_iDepth] );
498 	}
499 };
500 
501 static MemCategoryStack_t * g_pMainTLS = NULL; // category stack of main thread
502 
503 // memory statistic's per thread factory
sphMemStatThdInit()504 static MemCategoryStack_t * sphMemStatThdInit ()
505 {
506 	MemCategoryStack_t * pTLS = (MemCategoryStack_t *)sphDebugNew ( sizeof ( MemCategoryStack_t ) );
507 	pTLS->Reset();
508 
509 	Verify ( sphThreadSet ( g_tTLSMemCategory, pTLS ) );
510 	return pTLS;
511 }
512 
513 // per thread cleanup of memory statistic's
sphMemStatThdCleanup(MemCategoryStack_t * pTLS)514 static void sphMemStatThdCleanup ( MemCategoryStack_t * pTLS )
515 {
516 	sphDebugDelete ( pTLS );
517 }
518 
519 // init of memory statistic's data
sphMemStatInit()520 static void sphMemStatInit ()
521 {
522 	Verify ( sphThreadKeyCreate ( &g_tTLSMemCategory ) );
523 
524 	// main thread statistic's creation
525 	assert ( g_pMainTLS==NULL );
526 	g_pMainTLS = sphMemStatThdInit();
527 	assert ( g_pMainTLS!=NULL );
528 }
529 
530 // cleanup of memory statistic's data
sphMemStatDone()531 static void sphMemStatDone ()
532 {
533 	assert ( g_pMainTLS!=NULL );
534 	sphMemStatThdCleanup ( g_pMainTLS );
535 
536 	sphThreadKeyDelete ( g_tTLSMemCategory );
537 }
538 
539 // direct access for special category
sphMemStatMMapAdd(int64_t iSize)540 void sphMemStatMMapAdd ( int64_t iSize )
541 {
542 	g_tAllocsMutex.Lock ();
543 
544 	g_iCurAllocs++;
545 	g_iCurBytes += iSize;
546 	g_iTotalAllocs++;
547 	g_iPeakAllocs = Max ( g_iCurAllocs, g_iPeakAllocs );
548 	g_iPeakBytes = Max ( g_iCurBytes, g_iPeakBytes );
549 
550 	g_dMemCategoryStat[MEM_MMAPED].m_iSize += iSize;
551 	g_dMemCategoryStat[MEM_MMAPED].m_iCount++;
552 
553 	g_tAllocsMutex.Unlock ();
554 }
555 
sphMemStatMMapDel(int64_t iSize)556 void sphMemStatMMapDel ( int64_t iSize )
557 {
558 	g_tAllocsMutex.Lock ();
559 
560 	g_iCurAllocs--;
561 	g_iCurBytes -= iSize;
562 
563 	g_dMemCategoryStat[MEM_MMAPED].m_iSize -= iSize;
564 	g_dMemCategoryStat[MEM_MMAPED].m_iCount--;
565 
566 	g_tAllocsMutex.Unlock ();
567 }
568 
569 // push new category on arrival
sphMemStatPush(MemCategory_e eCategory)570 void sphMemStatPush ( MemCategory_e eCategory )
571 {
572 	MemCategoryStack_t * pTLS = (MemCategoryStack_t*) sphThreadGet ( g_tTLSMemCategory );
573 	if ( pTLS )
574 		pTLS->Push ( eCategory );
575 };
576 
577 // restore last category
sphMemStatPop(MemCategory_e eCategory)578 void sphMemStatPop ( MemCategory_e eCategory )
579 {
580 	MemCategoryStack_t * pTLS = (MemCategoryStack_t*) sphThreadGet ( g_tTLSMemCategory );
581 	if ( pTLS )
582 		pTLS->Pop ( eCategory );
583 };
584 
585 // get current category
sphMemStatGet()586 static MemCategory_e sphMemStatGet ()
587 {
588 	MemCategoryStack_t * pTLS = (MemCategoryStack_t*) sphThreadGet ( g_tTLSMemCategory );
589 	return pTLS ? pTLS->Top() : MEM_CORE;
590 }
591 
592 
593 // human readable category names
594 #define MEM_CATEGORY(_arg) #_arg
595 static const char* g_dMemCategoryName[] = { MEM_CATEGORIES };
596 #undef MEM_CATEGORY
597 
598 
sphMemStatDump(int iFD)599 void sphMemStatDump ( int iFD )
600 {
601 	int64_t iSize = 0;
602 	int iCount = 0;
603 	for ( int i=0; i<MEM_TOTAL; i++ )
604 	{
605 		iSize += (int64_t) g_dMemCategoryStat[i].m_iSize;
606 		iCount += g_dMemCategoryStat[i].m_iCount;
607 	}
608 
609 	sphSafeInfo ( iFD, "%-24s allocs-count=%d, mem-total=%d.%d Mb", "(total)", iCount,
610 		(int)(iSize/1048576), (int)( (iSize*10/1048576)%10 ) );
611 
612 	for ( int i=0; i<MEM_TOTAL; i++ )
613 		if ( g_dMemCategoryStat[i].m_iCount>0 )
614 	{
615 		iSize = (int64_t) g_dMemCategoryStat[i].m_iSize;
616 		sphSafeInfo ( iFD, "%-24s allocs-count=%d, mem-total=%d.%d Mb",
617 			g_dMemCategoryName[i], g_dMemCategoryStat[i].m_iCount,
618 			(int)(iSize/1048576), (int)( (iSize*10/1048576)%10 ) );
619 	}
620 }
621 
622 //////////////////////////////////////////////////////////////////////////////
623 // PRODUCTION MEMORY MANAGER
624 //////////////////////////////////////////////////////////////////////////////
625 
626 #else
627 #ifndef SPH_DONT_OVERRIDE_MEMROUTINES
628 
operator new(size_t iSize)629 void * operator new ( size_t iSize )
630 {
631 	void * pResult = ::malloc ( iSize );
632 	if ( !pResult )
633 		sphDieRestart ( "out of memory (unable to allocate " UINT64_FMT " bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
634 	return pResult;
635 }
636 
637 
operator new[](size_t iSize)638 void * operator new [] ( size_t iSize )
639 {
640 	void * pResult = ::malloc ( iSize );
641 	if ( !pResult )
642 		sphDieRestart ( "out of memory (unable to allocate " UINT64_FMT " bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
643 	return pResult;
644 }
645 
646 #if USE_RE2
operator delete(void * pPtr)647 void operator delete ( void * pPtr ) throw ()
648 #else
649 void operator delete ( void * pPtr )
650 #endif
651 {
652 	if ( pPtr )
653 		::free ( pPtr );
654 }
655 
656 
657 #if USE_RE2
operator delete[](void * pPtr)658 void operator delete [] ( void * pPtr ) throw ()
659 #else
660 void operator delete [] ( void * pPtr )
661 #endif
662 {
663 	if ( pPtr )
664 		::free ( pPtr );
665 }
666 
667 #endif // SPH_DONT_OVERRIDE_MEMROUTINES
668 #endif // SPH_ALLOCS_PROFILER
669 #endif // SPH_DEBUG_LEAKS
670 
671 //////////////////////////////////////////////////////////////////////////
672 
673 // now let the rest of sphinxstd use proper new
674 #if SPH_DEBUG_LEAKS || SPH_ALLOCS_PROFILER
675 #undef new
676 #define new		new(__FILE__,__LINE__)
677 #endif
678 
679 /////////////////////////////////////////////////////////////////////////////
680 // HELPERS
681 /////////////////////////////////////////////////////////////////////////////
682 
683 static SphDieCallback_t g_pfDieCallback = NULL;
684 
685 
sphSetDieCallback(SphDieCallback_t pfDieCallback)686 void sphSetDieCallback ( SphDieCallback_t pfDieCallback )
687 {
688 	g_pfDieCallback = pfDieCallback;
689 }
690 
691 
sphDie(const char * sTemplate,...)692 void sphDie ( const char * sTemplate, ... )
693 {
694 	char sBuf[1024];
695 
696 	va_list ap;
697 	va_start ( ap, sTemplate );
698 	vsnprintf ( sBuf, sizeof(sBuf), sTemplate, ap );
699 	va_end ( ap );
700 
701 	// if there's no callback,
702 	// or if callback returns true,
703 	// log to stdout
704 	if ( !g_pfDieCallback || g_pfDieCallback ( sBuf ) )
705 		fprintf ( stdout, "FATAL: %s\n", sBuf );
706 
707 	exit ( 1 );
708 }
709 
710 
sphDieRestart(const char * sTemplate,...)711 void sphDieRestart ( const char * sTemplate, ... )
712 {
713 	char sBuf[1024];
714 
715 	va_list ap;
716 	va_start ( ap, sTemplate );
717 	vsnprintf ( sBuf, sizeof(sBuf), sTemplate, ap );
718 	va_end ( ap );
719 
720 	// if there's no callback,
721 	// or if callback returns true,
722 	// log to stdout
723 	if ( !g_pfDieCallback || g_pfDieCallback ( sBuf ) )
724 		fprintf ( stdout, "FATAL: %s\n", sBuf );
725 
726 	exit ( 2 ); // almost CRASH_EXIT
727 }
728 
729 //////////////////////////////////////////////////////////////////////////
730 // RANDOM NUMBERS GENERATOR
731 //////////////////////////////////////////////////////////////////////////
732 
733 /// MWC (Multiply-With-Carry) RNG, invented by George Marsaglia
734 static DWORD g_dRngState[5] = { 0x95d3474bUL, 0x035cf1f7UL, 0xfd43995fUL, 0x5dfc55fbUL, 0x334a9229UL };
735 
736 
737 /// seed
sphSrand(DWORD uSeed)738 void sphSrand ( DWORD uSeed )
739 {
740 	for ( int i=0; i<5; i++ )
741 	{
742 		uSeed = uSeed*29943829 - 1;
743 		g_dRngState[i] = uSeed;
744 	}
745 	for ( int i=0; i<19; i++ )
746 		sphRand();
747 }
748 
749 
750 /// auto-seed RNG based on time and PID
sphAutoSrand()751 void sphAutoSrand ()
752 {
753 	// get timestamp
754 #if !USE_WINDOWS
755 	struct timeval tv;
756 	gettimeofday ( &tv, NULL );
757 #else
758 	#define getpid() GetCurrentProcessId()
759 
760 	struct
761 	{
762 		time_t	tv_sec;
763 		DWORD	tv_usec;
764 	} tv;
765 
766 	FILETIME ft;
767 	GetSystemTimeAsFileTime ( &ft );
768 
769 	uint64_t ts = ( uint64_t(ft.dwHighDateTime)<<32 ) + uint64_t(ft.dwLowDateTime) - 116444736000000000ULL; // Jan 1, 1970 magic
770 	ts /= 10; // to microseconds
771 	tv.tv_sec = (DWORD)(ts/1000000);
772 	tv.tv_usec = (DWORD)(ts%1000000);
773 #endif
774 
775 	// twist and shout
776 	sphSrand ( sphRand() ^ DWORD(tv.tv_sec) ^ (DWORD(tv.tv_usec) + DWORD(getpid())) );
777 }
778 
779 
780 /// generate another dword
sphRand()781 DWORD sphRand ()
782 {
783 	uint64_t uSum;
784 	uSum =
785 		(uint64_t)g_dRngState[0] * (uint64_t)5115 +
786 		(uint64_t)g_dRngState[1] * (uint64_t)1776 +
787 		(uint64_t)g_dRngState[2] * (uint64_t)1492 +
788 		(uint64_t)g_dRngState[3] * (uint64_t)2111111111UL +
789 		(uint64_t)g_dRngState[4];
790 	g_dRngState[3] = g_dRngState[2];
791 	g_dRngState[2] = g_dRngState[1];
792 	g_dRngState[1] = g_dRngState[0];
793 	g_dRngState[4] = (DWORD)( uSum>>32 );
794 	g_dRngState[0] = (DWORD)uSum;
795 	return g_dRngState[0];
796 }
797 
798 //////////////////////////////////////////////////////////////////////////
799 
800 #if !USE_WINDOWS
CSphProcessSharedMutex(int iExtraSize)801 CSphProcessSharedMutex::CSphProcessSharedMutex ( int iExtraSize )
802 {
803 	m_pMutex = NULL;
804 
805 #ifdef __FreeBSD__
806 	CSphString sError, sWarning;
807 	if ( !m_pStorage.Alloc ( sizeof(sem_t) + iExtraSize, sError, sWarning ) )
808 	{
809 		m_sError.SetSprintf ( "storage.alloc, error='%s', warning='%s'", sError.cstr(), sWarning.cstr() );
810 		return;
811 	}
812 
813 	m_pMutex = (sem_t*) m_pStorage.GetWritePtr ();
814 	int iRes = sem_init ( m_pMutex, 1, 1 );
815 	if ( iRes )
816 	{
817 		m_sError.SetSprintf ( "sem_init, errno=%d ", iRes );
818 		m_pMutex = NULL;
819 		m_pStorage.Reset ();
820 		return;
821 	}
822 #else
823 	pthread_mutexattr_t tAttr;
824 	int iRes = pthread_mutexattr_init ( &tAttr );
825 	if ( iRes )
826 	{
827 		m_sError.SetSprintf ( "pthread_mutexattr_init, errno=%d", iRes );
828 		return;
829 	}
830 	iRes = pthread_mutexattr_setpshared ( &tAttr, PTHREAD_PROCESS_SHARED );
831 	if ( iRes )
832 	{
833 		m_sError.SetSprintf ( "pthread_mutexattr_setpshared, errno = %d", iRes );
834 		pthread_mutexattr_destroy ( &tAttr );
835 		return;
836 	}
837 
838 	CSphString sError, sWarning;
839 	if ( !m_pStorage.Alloc ( sizeof(pthread_mutex_t) + iExtraSize, sError, sWarning ) )
840 	{
841 		m_sError.SetSprintf ( "storage.alloc, error='%s', warning='%s'", sError.cstr(), sWarning.cstr() );
842 		pthread_mutexattr_destroy ( &tAttr );
843 		return;
844 	}
845 
846 	m_pMutex = (pthread_mutex_t*) m_pStorage.GetWritePtr ();
847 	iRes = pthread_mutex_init ( m_pMutex, &tAttr );
848 
849 	if ( iRes )
850 	{
851 		m_sError.SetSprintf ( "pthread_mutex_init, errno=%d ", iRes );
852 		pthread_mutexattr_destroy ( &tAttr );
853 		m_pMutex = NULL;
854 		m_pStorage.Reset ();
855 		return;
856 	}
857 
858 	iRes = pthread_mutexattr_destroy ( &tAttr );
859 	if ( iRes )
860 	{
861 		m_sError.SetSprintf ( "pthread_mutexattr_destroy, errno = %d", iRes );
862 		return;
863 	}
864 #endif // __FreeBSD__
865 }
866 
~CSphProcessSharedMutex()867 CSphProcessSharedMutex::~CSphProcessSharedMutex()
868 {
869 	if ( m_pMutex )
870 	{
871 #ifdef __FreeBSD__
872 		sem_destroy ( m_pMutex );
873 #else
874 		pthread_mutex_destroy ( m_pMutex );
875 #endif
876 		m_pMutex = NULL;
877 	}
878 }
879 #else
CSphProcessSharedMutex(int)880 CSphProcessSharedMutex::CSphProcessSharedMutex ( int )
881 {
882 	m_tLock.Init();
883 }
884 
~CSphProcessSharedMutex()885 CSphProcessSharedMutex::~CSphProcessSharedMutex()
886 {
887 	m_tLock.Done();
888 }
889 #endif
890 
891 
Lock()892 void CSphProcessSharedMutex::Lock ()
893 {
894 #if !USE_WINDOWS
895 #ifdef __FreeBSD__
896 	if ( m_pMutex )
897 		sem_wait ( m_pMutex );
898 #else
899 	if ( m_pMutex )
900 		pthread_mutex_lock ( m_pMutex );
901 #endif
902 #else
903 	m_tLock.Lock();
904 #endif
905 }
906 
907 
Unlock()908 void CSphProcessSharedMutex::Unlock ()
909 {
910 #if !USE_WINDOWS
911 #ifdef __FreeBSD__
912 	if ( m_pMutex )
913 		sem_post ( m_pMutex );
914 #else
915 	if ( m_pMutex )
916 		pthread_mutex_unlock ( m_pMutex );
917 #endif
918 #else
919 	m_tLock.Unlock();
920 #endif
921 }
922 
923 
924 #if USE_WINDOWS
TimedLock(int) const925 bool CSphProcessSharedMutex::TimedLock ( int ) const
926 	{
927 	return false;
928 #else
929 bool CSphProcessSharedMutex::TimedLock ( int tmSpin ) const
930 {
931 	if ( !m_pMutex )
932 		return false;
933 
934 #ifdef __FreeBSD__
935 	struct timespec tp;
936 	clock_gettime ( CLOCK_REALTIME, &tp );
937 
938 	tp.tv_nsec += tmSpin * 1000;
939 	if ( tp.tv_nsec > 1000000 )
940 	{
941 		int iDelta = (int)( tp.tv_nsec / 1000000 );
942 		tp.tv_sec += iDelta * 1000000;
943 		tp.tv_nsec -= iDelta * 1000000;
944 	}
945 
946 	return ( sem_timedwait ( m_pMutex, &tp )==0 );
947 #else
948 #if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) && defined(HAVE_CLOCK_GETTIME)
949 	struct timespec tp;
950 	clock_gettime ( CLOCK_REALTIME, &tp );
951 
952 	tp.tv_nsec += tmSpin * 1000;
953 	if ( tp.tv_nsec > 1000000 )
954 	{
955 		int iDelta = (int)( tp.tv_nsec / 1000000 );
956 		tp.tv_sec += iDelta * 1000000;
957 		tp.tv_nsec -= iDelta * 1000000;
958 	}
959 
960 	return ( pthread_mutex_timedlock ( m_pMutex, &tp )==0 );
961 #else
962 	int iRes = EBUSY;
963 	int64_t tmTill = sphMicroTimer() + tmSpin;
964 	do
965 	{
966 		iRes = pthread_mutex_trylock ( m_pMutex );
967 		if ( iRes==EBUSY )
968 			sphSleepMsec ( 0 );
969 	} while ( iRes==EBUSY && sphMicroTimer()<tmTill );
970 
971 	if ( iRes==EBUSY )
972 		iRes = pthread_mutex_trylock ( m_pMutex );
973 
974 	return iRes==0;
975 #endif // HAVE_PTHREAD_MUTEX_TIMEDLOCK && HAVE_CLOCK_GETTIME
976 #endif // __FreeBSD__
977 #endif // USE_WINDOWS
978 }
979 
980 
981 BYTE * CSphProcessSharedMutex::GetSharedData() const
982 {
983 #if !USE_WINDOWS
984 #ifdef __FreeBSD__
985 	return m_pStorage.GetWritePtr () + sizeof ( sem_t );
986 #else
987 	return m_pStorage.GetWritePtr () + sizeof ( pthread_mutex_t );
988 #endif
989 #else
990 	return NULL;
991 #endif
992 }
993 
994 
995 const char * CSphProcessSharedMutex::GetError() const
996 {
997 	const char * sError = NULL;
998 #if !USE_WINDOWS
999 	sError = m_sError.cstr();
1000 #endif
1001 	return sError;
1002 }
1003 
1004 
1005 //////////////////////////////////////////////////////////////////////////
1006 // THREADING FUNCTIONS
1007 //////////////////////////////////////////////////////////////////////////
1008 
1009 // This is a working context for a thread wrapper. It wraps every thread to
1010 // store information about it's stack size, cleanup threads and something else.
1011 // This struct always should be allocated in the heap, cause wrapper need
1012 // to see it all the time and it frees it out of the heap by itself. Wrapper thread function
1013 // receives as an argument a pointer to ThreadCall_t with one function pointer to
1014 // a main thread function. Afterwards, thread can set up one or more cleanup functions
1015 // which will be executed by a wrapper in the linked list order after it dies.
1016 struct ThreadCall_t
1017 {
1018 	void			( *m_pCall )( void * pArg );
1019 	void *			m_pArg;
1020 #if USE_GPROF
1021 	pthread_mutex_t	m_dlock;
1022 	pthread_cond_t	m_dwait;
1023 	itimerval		m_ditimer;
1024 #endif
1025 	ThreadCall_t *	m_pNext;
1026 };
1027 static SphThreadKey_t g_tThreadCleanupKey;
1028 static SphThreadKey_t g_tMyThreadStack;
1029 
1030 
1031 #if USE_WINDOWS
1032 #define SPH_THDFUNC DWORD __stdcall
1033 #else
1034 #define SPH_THDFUNC void *
1035 #endif
1036 
1037 SPH_THDFUNC sphThreadProcWrapper ( void * pArg )
1038 {
1039 	// This is the first local variable in the new thread. So, its address is the top of the stack.
1040 	// We need to know thread stack size for both expression and query evaluating engines.
1041 	// We store expressions as a linked tree of structs and execution is a calls of mutually
1042 	// recursive methods. Before executing we compute tree height and multiply it by a constant
1043 	// with experimentally measured value to check whether we have enough stack to execute current query.
1044 	// The check is not ideal and do not work for all compilers and compiler settings.
1045 	char	cTopOfMyStack;
1046 	assert ( sphThreadGet ( g_tThreadCleanupKey )==NULL );
1047 	assert ( sphThreadGet ( g_tMyThreadStack )==NULL );
1048 
1049 #if SPH_ALLOCS_PROFILER
1050 	MemCategoryStack_t * pTLS = sphMemStatThdInit();
1051 #endif
1052 
1053 #if USE_GPROF
1054 	// Set the profile timer value
1055 	setitimer ( ITIMER_PROF, &( (ThreadCall_t*) pArg )->m_ditimer, NULL );
1056 
1057 	// Tell the calling thread that we don't need its data anymore
1058 	pthread_mutex_lock ( &( (ThreadCall_t*) pArg)->m_dlock );
1059 	pthread_cond_signal ( &( (ThreadCall_t*) pArg)->m_dwait );
1060 	pthread_mutex_unlock ( &( (ThreadCall_t*) pArg)->m_dlock );
1061 #endif
1062 
1063 	ThreadCall_t * pCall = (ThreadCall_t*) pArg;
1064 	MemorizeStack ( & cTopOfMyStack );
1065 	pCall->m_pCall ( pCall->m_pArg );
1066 	SafeDelete ( pCall );
1067 
1068 	ThreadCall_t * pCleanup = (ThreadCall_t*) sphThreadGet ( g_tThreadCleanupKey );
1069 	while ( pCleanup )
1070 	{
1071 		pCall = pCleanup;
1072 		pCall->m_pCall ( pCall->m_pArg );
1073 		pCleanup = pCall->m_pNext;
1074 		SafeDelete ( pCall );
1075 	}
1076 
1077 #if SPH_ALLOCS_PROFILER
1078 	sphMemStatThdCleanup ( pTLS );
1079 #endif
1080 
1081 	return 0;
1082 }
1083 
1084 #if !USE_WINDOWS
1085 void * sphThreadInit ( bool bDetached )
1086 #else
1087 void * sphThreadInit ( bool )
1088 #endif
1089 {
1090 	static bool bInit = false;
1091 #if !USE_WINDOWS
1092 	static pthread_attr_t tJoinableAttr;
1093 	static pthread_attr_t tDetachedAttr;
1094 #endif
1095 
1096 	if ( !bInit )
1097 	{
1098 #if SPH_DEBUG_LEAKS || SPH_ALLOCS_PROFILER
1099 		sphMemStatInit();
1100 #endif
1101 
1102 		// we're single-threaded yet, right?!
1103 		if ( !sphThreadKeyCreate ( &g_tThreadCleanupKey ) )
1104 			sphDie ( "FATAL: sphThreadKeyCreate() failed" );
1105 
1106 		if ( !sphThreadKeyCreate ( &g_tMyThreadStack ) )
1107 			sphDie ( "FATAL: sphThreadKeyCreate() failed" );
1108 
1109 #if !USE_WINDOWS
1110 		if ( pthread_attr_init ( &tJoinableAttr ) )
1111 			sphDie ( "FATAL: pthread_attr_init( joinable ) failed" );
1112 
1113 		if ( pthread_attr_init ( &tDetachedAttr ) )
1114 			sphDie ( "FATAL: pthread_attr_init( detached ) failed" );
1115 
1116 		if ( pthread_attr_setdetachstate ( &tDetachedAttr, PTHREAD_CREATE_DETACHED ) )
1117 			sphDie ( "FATAL: pthread_attr_setdetachstate( detached ) failed" );
1118 #endif
1119 		bInit = true;
1120 	}
1121 #if !USE_WINDOWS
1122 	if ( pthread_attr_setstacksize ( &tJoinableAttr, g_iThreadStackSize + PTHREAD_STACK_MIN ) )
1123 		sphDie ( "FATAL: pthread_attr_setstacksize( joinable ) failed" );
1124 
1125 	if ( pthread_attr_setstacksize ( &tDetachedAttr, g_iThreadStackSize + PTHREAD_STACK_MIN ) )
1126 		sphDie ( "FATAL: pthread_attr_setstacksize( detached ) failed" );
1127 
1128 	return bDetached ? &tDetachedAttr : &tJoinableAttr;
1129 #else
1130 	return NULL;
1131 #endif
1132 }
1133 
1134 
1135 #if SPH_DEBUG_LEAKS || SPH_ALLOCS_PROFILER
1136 void sphThreadDone ( int iFD )
1137 {
1138 	sphMemStatDump ( iFD );
1139 	sphMemStatDone();
1140 }
1141 #else
1142 void sphThreadDone ( int )
1143 {
1144 }
1145 #endif
1146 
1147 
1148 bool sphThreadCreate ( SphThread_t * pThread, void (*fnThread)(void*), void * pArg, bool bDetached )
1149 {
1150 	// we can not put this on current stack because wrapper need to see
1151 	// it all the time and it will destroy this data from heap by itself
1152 	ThreadCall_t * pCall = new ThreadCall_t;
1153 	pCall->m_pCall = fnThread;
1154 	pCall->m_pArg = pArg;
1155 	pCall->m_pNext = NULL;
1156 
1157 	// create thread
1158 #if USE_WINDOWS
1159 	sphThreadInit ( bDetached );
1160 	*pThread = CreateThread ( NULL, g_iThreadStackSize, sphThreadProcWrapper, pCall, 0, NULL );
1161 	if ( *pThread )
1162 		return true;
1163 #else
1164 
1165 #if USE_GPROF
1166 	getitimer ( ITIMER_PROF, &pCall->m_ditimer );
1167 	pthread_cond_init ( &pCall->m_dwait, NULL );
1168 	pthread_mutex_init ( &pCall->m_dlock, NULL );
1169 	pthread_mutex_lock ( &pCall->m_dlock );
1170 #endif
1171 
1172 	void * pAttr = sphThreadInit ( bDetached );
1173 	errno = pthread_create ( pThread, (pthread_attr_t*) pAttr, sphThreadProcWrapper, pCall );
1174 
1175 #if USE_GPROF
1176 	if ( !errno )
1177 		pthread_cond_wait ( &pCall->m_dwait, &pCall->m_dlock );
1178 
1179 	pthread_mutex_unlock ( &pCall->m_dlock );
1180 	pthread_mutex_destroy ( &pCall->m_dlock );
1181 	pthread_cond_destroy ( &pCall->m_dwait );
1182 #endif
1183 
1184 	if ( !errno )
1185 		return true;
1186 
1187 #endif
1188 
1189 	// thread creation failed so we need to cleanup ourselves
1190 	SafeDelete ( pCall );
1191 	return false;
1192 }
1193 
1194 
1195 bool sphThreadJoin ( SphThread_t * pThread )
1196 {
1197 #if USE_WINDOWS
1198 	DWORD uWait = WaitForSingleObject ( *pThread, INFINITE );
1199 	CloseHandle ( *pThread );
1200 	*pThread = NULL;
1201 	return ( uWait==WAIT_OBJECT_0 || uWait==WAIT_ABANDONED );
1202 #else
1203 	return pthread_join ( *pThread, NULL )==0;
1204 #endif
1205 }
1206 
1207 // Adds a function call (a new task for a wrapper) to a linked list
1208 // of thread contexts. They will be executed one by one right after
1209 // the main thread ends its execution. This is a way for a wrapper
1210 // to free local resources allocated by its main thread.
1211 void sphThreadOnExit ( void (*fnCleanup)(void*), void * pArg )
1212 {
1213 	ThreadCall_t * pCleanup = new ThreadCall_t;
1214 	pCleanup->m_pCall = fnCleanup;
1215 	pCleanup->m_pArg = pArg;
1216 	pCleanup->m_pNext = (ThreadCall_t*) sphThreadGet ( g_tThreadCleanupKey );
1217 	sphThreadSet ( g_tThreadCleanupKey, pCleanup );
1218 }
1219 
1220 
1221 bool sphThreadKeyCreate ( SphThreadKey_t * pKey )
1222 {
1223 #if USE_WINDOWS
1224 	*pKey = TlsAlloc();
1225 	return *pKey!=TLS_OUT_OF_INDEXES;
1226 #else
1227 	return pthread_key_create ( pKey, NULL )==0;
1228 #endif
1229 }
1230 
1231 
1232 void sphThreadKeyDelete ( SphThreadKey_t tKey )
1233 {
1234 #if USE_WINDOWS
1235 	TlsFree ( tKey );
1236 #else
1237 	pthread_key_delete ( tKey );
1238 #endif
1239 }
1240 
1241 
1242 void * sphThreadGet ( SphThreadKey_t tKey )
1243 {
1244 #if USE_WINDOWS
1245 	return TlsGetValue ( tKey );
1246 #else
1247 	return pthread_getspecific ( tKey );
1248 #endif
1249 }
1250 
1251 void * sphMyStack ()
1252 {
1253 	return sphThreadGet ( g_tMyThreadStack );
1254 }
1255 
1256 
1257 int64_t sphGetStackUsed()
1258 {
1259 	BYTE cStack;
1260 	BYTE * pStackTop = (BYTE*)sphMyStack();
1261 	if ( !pStackTop )
1262 		return 0;
1263 	int64_t iHeight = pStackTop - &cStack;
1264 	return ( iHeight>=0 ) ? iHeight : -iHeight;
1265 }
1266 
1267 void sphSetMyStackSize ( int iStackSize )
1268 {
1269 	g_iThreadStackSize = iStackSize;
1270 	sphThreadInit ( false );
1271 }
1272 
1273 
1274 void MemorizeStack ( void* PStack )
1275 {
1276 	sphThreadSet ( g_tMyThreadStack, PStack );
1277 }
1278 
1279 
1280 bool sphThreadSet ( SphThreadKey_t tKey, void * pValue )
1281 {
1282 #if USE_WINDOWS
1283 	return TlsSetValue ( tKey, pValue )!=FALSE;
1284 #else
1285 	return pthread_setspecific ( tKey, pValue )==0;
1286 #endif
1287 }
1288 
1289 #if !USE_WINDOWS
1290 bool sphIsLtLib()
1291 {
1292 #ifndef _CS_GNU_LIBPTHREAD_VERSION
1293 	return false;
1294 #else
1295 	char buff[64];
1296 	confstr ( _CS_GNU_LIBPTHREAD_VERSION, buff, 64 );
1297 
1298 	if ( !strncasecmp ( buff, "linuxthreads", 12 ) )
1299 		return true;
1300 	return false;
1301 #endif
1302 }
1303 #endif
1304 
1305 //////////////////////////////////////////////////////////////////////////
1306 // MUTEX and EVENT
1307 //////////////////////////////////////////////////////////////////////////
1308 
1309 #if USE_WINDOWS
1310 
1311 // Windows mutex implementation
1312 
1313 bool CSphMutex::Init ()
1314 {
1315 	assert ( !m_bInitialized );
1316 	m_hMutex = CreateMutex ( NULL, FALSE, NULL );
1317 	m_bInitialized = ( m_hMutex!=NULL );
1318 	return m_bInitialized;
1319 }
1320 
1321 bool CSphMutex::Done ()
1322 {
1323 	if ( !m_bInitialized )
1324 		return true;
1325 
1326 	m_bInitialized = false;
1327 	return CloseHandle ( m_hMutex )==TRUE;
1328 }
1329 
1330 bool CSphMutex::Lock ()
1331 {
1332 	assert ( m_bInitialized );
1333 	DWORD uWait = WaitForSingleObject ( m_hMutex, INFINITE );
1334 	return ( uWait!=WAIT_FAILED && uWait!=WAIT_TIMEOUT );
1335 }
1336 
1337 bool CSphMutex::Unlock ()
1338 {
1339 	assert ( m_bInitialized );
1340 	return ReleaseMutex ( m_hMutex )==TRUE;
1341 }
1342 
1343 bool CSphAutoEvent::Init ( CSphMutex * )
1344 {
1345 	m_bSent = false;
1346 	m_hEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
1347 	m_bInitialized = ( m_hEvent!=0 );
1348 		return m_bInitialized;
1349 }
1350 
1351 bool CSphAutoEvent::Done()
1352 {
1353 	if ( !m_bInitialized )
1354 		return true;
1355 
1356 	m_bInitialized = false;
1357 	return CloseHandle ( m_hEvent )==TRUE;
1358 }
1359 
1360 void CSphAutoEvent::SetEvent()
1361 {
1362 	::SetEvent ( m_hEvent );
1363 	m_bSent = true;
1364 }
1365 
1366 bool CSphAutoEvent::WaitEvent()
1367 {
1368 	if ( m_bSent )
1369 	{
1370 		m_bSent = false;
1371 		return true;
1372 	}
1373 	DWORD uWait = WaitForSingleObject ( m_hEvent, INFINITE );
1374 	return !( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT );
1375 }
1376 
1377 #else
1378 
1379 // UNIX mutex implementation
1380 
1381 bool CSphMutex::Init ()
1382 {
1383 	assert ( !m_bInitialized );
1384 	m_bInitialized = ( pthread_mutex_init ( &m_tMutex, NULL )==0 );
1385 	return m_bInitialized;
1386 }
1387 
1388 bool CSphMutex::Done ()
1389 {
1390 	if ( !m_bInitialized )
1391 		return true;
1392 
1393 	m_bInitialized = false;
1394 	return pthread_mutex_destroy ( &m_tMutex )==0;
1395 }
1396 
1397 bool CSphMutex::Lock ()
1398 {
1399 	assert ( m_bInitialized );
1400 	return ( pthread_mutex_lock ( &m_tMutex )==0 );
1401 }
1402 
1403 bool CSphMutex::Unlock ()
1404 {
1405 	assert ( m_bInitialized );
1406 	return ( pthread_mutex_unlock ( &m_tMutex )==0 );
1407 }
1408 
1409 bool CSphAutoEvent::Init ( CSphMutex * pMutex )
1410 {
1411 	m_bSent = false;
1412 	assert ( pMutex );
1413 	if ( !pMutex )
1414 		return false;
1415 	m_pMutex = pMutex->GetInternalMutex();
1416 	m_bInitialized = ( pthread_cond_init ( &m_tCond, NULL )==0 );
1417 	return m_bInitialized;
1418 }
1419 
1420 bool CSphAutoEvent::Done ()
1421 {
1422 	if ( !m_bInitialized )
1423 		return true;
1424 
1425 	m_bInitialized = false;
1426 	return ( pthread_cond_destroy ( &m_tCond ) )==0;
1427 }
1428 
1429 void CSphAutoEvent::SetEvent ()
1430 {
1431 	if ( !m_bInitialized )
1432 		return;
1433 
1434 	pthread_cond_signal ( &m_tCond ); // locking is done from outside
1435 	m_bSent = true;
1436 }
1437 
1438 bool CSphAutoEvent::WaitEvent ()
1439 {
1440 	if ( !m_bInitialized )
1441 		return true;
1442 	pthread_mutex_lock ( m_pMutex );
1443 	if ( !m_bSent )
1444 		pthread_cond_wait ( &m_tCond, m_pMutex );
1445 	m_bSent = false;
1446 	pthread_mutex_unlock ( m_pMutex );
1447 	return true;
1448 }
1449 
1450 #endif
1451 
1452 //////////////////////////////////////////////////////////////////////////
1453 // RWLOCK
1454 //////////////////////////////////////////////////////////////////////////
1455 
1456 #if USE_WINDOWS
1457 
1458 // Windows rwlock implementation
1459 
1460 CSphRwlock::CSphRwlock ()
1461 	: m_bInitialized ( false )
1462 	, m_hWriteMutex ( NULL )
1463 	, m_hReadEvent ( NULL )
1464 	, m_iReaders ( 0 )
1465 {}
1466 
1467 
1468 bool CSphRwlock::Init ( bool )
1469 {
1470 	assert ( !m_bInitialized );
1471 	assert ( !m_hWriteMutex && !m_hReadEvent && !m_iReaders );
1472 
1473 	m_hReadEvent = CreateEvent ( NULL, TRUE, FALSE, NULL );
1474 	if ( !m_hReadEvent )
1475 		return false;
1476 
1477 	m_hWriteMutex = CreateMutex ( NULL, FALSE, NULL );
1478 	if ( !m_hWriteMutex )
1479 	{
1480 		CloseHandle ( m_hReadEvent );
1481 		m_hReadEvent = NULL;
1482 		return false;
1483 	}
1484 	m_bInitialized = true;
1485 	return true;
1486 }
1487 
1488 
1489 bool CSphRwlock::Done ()
1490 {
1491 	if ( !m_bInitialized )
1492 		return true;
1493 
1494 	if ( !CloseHandle ( m_hReadEvent ) )
1495 		return false;
1496 	m_hReadEvent = NULL;
1497 
1498 	if ( !CloseHandle ( m_hWriteMutex ) )
1499 		return false;
1500 	m_hWriteMutex = NULL;
1501 
1502 	m_iReaders = 0;
1503 	m_bInitialized = false;
1504 	return true;
1505 }
1506 
1507 
1508 const char * CSphRwlock::GetError () const
1509 {
1510 	return m_sError.cstr();
1511 }
1512 
1513 
1514 bool CSphRwlock::ReadLock ()
1515 {
1516 	assert ( m_bInitialized );
1517 
1518 	DWORD uWait = WaitForSingleObject ( m_hWriteMutex, INFINITE );
1519 	if ( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT )
1520 		return false;
1521 
1522 	// got the writer mutex, can't be locked for write
1523 	// so it's OK to add the reader lock, then free the writer mutex
1524 	// writer mutex also protects readers counter
1525 	InterlockedIncrement ( &m_iReaders );
1526 
1527 	// reset writer lock event, we just got ourselves a reader
1528 	if ( !ResetEvent ( m_hReadEvent ) )
1529 		return false;
1530 
1531 	// release writer lock
1532 	return ReleaseMutex ( m_hWriteMutex )==TRUE;
1533 }
1534 
1535 
1536 bool CSphRwlock::WriteLock ()
1537 {
1538 	assert ( m_bInitialized );
1539 
1540 	// try to acquire writer mutex
1541 	DWORD uWait = WaitForSingleObject ( m_hWriteMutex, INFINITE );
1542 	if ( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT )
1543 		return false;
1544 
1545 	// got the writer mutex, no pending readers, rock'n'roll
1546 	if ( !m_iReaders )
1547 		return true;
1548 
1549 	// got the writer mutex, but still have to wait for all readers to complete
1550 	uWait = WaitForSingleObject ( m_hReadEvent, INFINITE );
1551 	if ( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT )
1552 	{
1553 		// wait failed, well then, release writer mutex
1554 		ReleaseMutex ( m_hWriteMutex );
1555 		return false;
1556 	}
1557 	return true;
1558 }
1559 
1560 
1561 bool CSphRwlock::Unlock ()
1562 {
1563 	assert ( m_bInitialized );
1564 
1565 	// are we unlocking a writer?
1566 	if ( ReleaseMutex ( m_hWriteMutex ) )
1567 		return true; // yes we are
1568 
1569 	if ( GetLastError()!=ERROR_NOT_OWNER )
1570 		return false; // some unexpected error
1571 
1572 	// writer mutex wasn't mine; we must have a read lock
1573 	if ( !m_iReaders )
1574 		return true; // could this ever happen?
1575 
1576 	// atomically decrement reader counter
1577 	if ( InterlockedDecrement ( &m_iReaders ) )
1578 		return true; // there still are pending readers
1579 
1580 	// no pending readers, fire the event for write lock
1581 	return SetEvent ( m_hReadEvent )==TRUE;
1582 }
1583 
1584 #else
1585 
1586 // UNIX rwlock implementation (pthreads wrapper)
1587 
1588 CSphRwlock::CSphRwlock ()
1589 	: m_bInitialized ( false )
1590 {}
1591 
1592 bool CSphRwlock::Init ( bool bProcessShared )
1593 {
1594 	assert ( !m_bInitialized );
1595 
1596 #ifdef __FreeBSD__
1597 	if ( bProcessShared )
1598 	{
1599 		m_sError = "process shared rwlock is not supported by FreeBSD";
1600 		return false;
1601 	}
1602 #endif
1603 
1604 	pthread_rwlockattr_t tAttr;
1605 	pthread_rwlockattr_t * pAttrUsed = NULL;
1606 	int iRes;
1607 
1608 	if ( bProcessShared )
1609 	{
1610 		iRes = pthread_rwlockattr_init ( &tAttr );
1611 		if ( iRes )
1612 		{
1613 			m_sError.SetSprintf ( "pthread_rwlockattr_init, errno=%d", iRes );
1614 			return false;
1615 		}
1616 		iRes = pthread_rwlockattr_setpshared ( &tAttr, PTHREAD_PROCESS_SHARED );
1617 		if ( iRes )
1618 		{
1619 			m_sError.SetSprintf ( "pthread_rwlockattr_setpshared, errno = %d", iRes );
1620 			pthread_rwlockattr_destroy ( &tAttr );
1621 			return false;
1622 		}
1623 
1624 		pAttrUsed = &tAttr;
1625 	}
1626 
1627 	iRes = pthread_rwlock_init ( &m_tLock, pAttrUsed );
1628 	if ( iRes )
1629 	{
1630 		m_sError.SetSprintf ( "pthread_rwlock_init, errno = %d", iRes );
1631 		if ( pAttrUsed )
1632 			pthread_rwlockattr_destroy ( pAttrUsed );
1633 		return false;
1634 	}
1635 
1636 	if ( pAttrUsed )
1637 	{
1638 		iRes = pthread_rwlockattr_destroy ( pAttrUsed );
1639 		if ( iRes )
1640 		{
1641 			m_sError.SetSprintf ( "pthread_rwlockattr_destroy, errno = %d", iRes );
1642 			return false;
1643 		}
1644 	}
1645 
1646 	m_bInitialized = true;
1647 
1648 	return true;
1649 }
1650 
1651 bool CSphRwlock::Done ()
1652 {
1653 	if ( !m_bInitialized )
1654 		return true;
1655 
1656 	m_bInitialized = !( pthread_rwlock_destroy ( &m_tLock )==0 );
1657 	return !m_bInitialized;
1658 }
1659 
1660 const char * CSphRwlock::GetError () const
1661 {
1662 	return m_sError.cstr();
1663 }
1664 
1665 bool CSphRwlock::ReadLock ()
1666 {
1667 	assert ( m_bInitialized );
1668 
1669 	return pthread_rwlock_rdlock ( &m_tLock )==0;
1670 }
1671 
1672 bool CSphRwlock::WriteLock ()
1673 {
1674 	assert ( m_bInitialized );
1675 
1676 	return pthread_rwlock_wrlock ( &m_tLock )==0;
1677 }
1678 
1679 bool CSphRwlock::Unlock ()
1680 {
1681 	assert ( m_bInitialized );
1682 
1683 	return pthread_rwlock_unlock ( &m_tLock )==0;
1684 }
1685 
1686 #endif
1687 
1688 //////////////////////////////////////////////////////////////////////////
1689 
1690 /// microsecond precision timestamp
1691 int64_t sphMicroTimer()
1692 {
1693 #if USE_WINDOWS
1694 	// Windows time query
1695 	static int64_t iBase = 0;
1696 	static int64_t iStart = 0;
1697 	static int64_t iFreq = 0;
1698 
1699 	LARGE_INTEGER iLarge;
1700 	if ( !iBase )
1701 	{
1702 		// get start QPC value
1703 		QueryPerformanceFrequency ( &iLarge ); iFreq = iLarge.QuadPart;
1704 		QueryPerformanceCounter ( &iLarge ); iStart = iLarge.QuadPart;
1705 
1706 		// get start UTC timestamp
1707 		// assuming it's still approximately the same moment as iStart, give or take a msec or three
1708 		FILETIME ft;
1709 		GetSystemTimeAsFileTime ( &ft );
1710 
1711 		iBase = ( int64_t(ft.dwHighDateTime)<<32 ) + int64_t(ft.dwLowDateTime);
1712 		iBase = ( iBase - 116444736000000000ULL ) / 10; // rebase from 01 Jan 1601 to 01 Jan 1970, and rescale to 1 usec from 100 ns
1713 	}
1714 
1715 	// we can't easily drag iBase into parens because iBase*iFreq/1000000 overflows 64bit int!
1716 	QueryPerformanceCounter ( &iLarge );
1717 	return iBase + ( iLarge.QuadPart - iStart )*1000000/iFreq;
1718 
1719 #else
1720 	// UNIX time query
1721 	struct timeval tv;
1722 	gettimeofday ( &tv, NULL );
1723 	return int64_t(tv.tv_sec)*int64_t(1000000) + int64_t(tv.tv_usec);
1724 #endif // USE_WINDOWS
1725 }
1726 
1727 //////////////////////////////////////////////////////////////////////////
1728 
1729 int CSphStrHashFunc::Hash ( const CSphString & sKey )
1730 {
1731 	return sKey.IsEmpty() ? 0 : sphCRC32 ( sKey.cstr() );
1732 }
1733 
1734 //////////////////////////////////////////////////////////////////////////
1735 
1736 DWORD g_dSphinxCRC32 [ 256 ] =
1737 {
1738 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
1739 	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
1740 	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
1741 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
1742 	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
1743 	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
1744 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
1745 	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
1746 	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
1747 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
1748 	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
1749 	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
1750 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
1751 	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
1752 	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
1753 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
1754 	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
1755 	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
1756 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
1757 	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
1758 	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
1759 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
1760 	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
1761 	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
1762 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
1763 	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
1764 	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
1765 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
1766 	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
1767 	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
1768 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
1769 	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
1770 	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
1771 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
1772 	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
1773 	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
1774 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
1775 	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
1776 	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
1777 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
1778 	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
1779 	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
1780 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
1781 	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
1782 	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
1783 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
1784 	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
1785 	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
1786 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
1787 	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
1788 	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
1789 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
1790 	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
1791 	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
1792 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
1793 	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
1794 	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
1795 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
1796 	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
1797 	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
1798 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
1799 	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
1800 	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
1801 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
1802 };
1803 
1804 
1805 DWORD sphCRC32 ( const void * s )
1806 {
1807 	// calc CRC
1808 	DWORD crc = ~((DWORD)0);
1809 	for ( const BYTE * p=(const BYTE*)s; *p; p++ )
1810 		crc = (crc >> 8) ^ g_dSphinxCRC32 [ (crc ^ (*p)) & 0xff ];
1811 	return ~crc;
1812 }
1813 
1814 DWORD sphCRC32 ( const void * s, int iLen )
1815 {
1816 	// calc CRC
1817 	DWORD crc = ~((DWORD)0);
1818 	const BYTE * p = (const BYTE*)s;
1819 	const BYTE * pMax = p + iLen;
1820 	while ( p<pMax )
1821 		crc = (crc >> 8) ^ g_dSphinxCRC32 [ (crc ^ *p++) & 0xff ];
1822 	return ~crc;
1823 }
1824 
1825 DWORD sphCRC32 ( const void * s, int iLen, DWORD uPrevCRC )
1826 {
1827 	// calc CRC
1828 	DWORD crc = ~((DWORD)uPrevCRC);
1829 	const BYTE * p = (const BYTE*)s;
1830 	const BYTE * pMax = p + iLen;
1831 	while ( p<pMax )
1832 		crc = (crc >> 8) ^ g_dSphinxCRC32 [ (crc ^ *p++) & 0xff ];
1833 	return ~crc;
1834 }
1835 
1836 #if USE_WINDOWS
1837 template<>
1838 CSphAtomic<long>::operator long()
1839 {
1840 	return InterlockedExchangeAdd ( &m_iValue, 0 );
1841 }
1842 template<>
1843 long CSphAtomic<long>::Inc()
1844 {
1845 	return InterlockedIncrement ( &m_iValue )-1;
1846 }
1847 template<>
1848 long CSphAtomic<long>::Dec()
1849 {
1850 	return InterlockedDecrement ( &m_iValue )+1;
1851 }
1852 #endif
1853 
1854 // fast check if we are built with right endianess settings
1855 const char*		sphCheckEndian()
1856 {
1857 	const char* sErrorMsg = "Oops! It seems that sphinx was built with wrong endianess (cross-compiling?)\n"
1858 #if USE_LITTLE_ENDIAN
1859 		"either reconfigure and rebuild, defining ac_cv_c_bigendian=yes in the environment of ./configure script,\n"
1860 		"either ensure that '#define USE_LITTLE_ENDIAN = 0' in config/config.h\n";
1861 #else
1862 		"either reconfigure and rebuild, defining ac_cv_c_bigendian=no in the environment of ./configure script,\n"
1863 		"either ensure that '#define USE_LITTLE_ENDIAN = 1' in config/config.h\n";
1864 #endif
1865 
1866 	char sMagic[] = "\x01\x02\x03\x04\x05\x06\x07\x08";
1867 	unsigned long *pMagic;
1868 	unsigned long uResult;
1869 	pMagic = (unsigned long*)sMagic;
1870 	uResult = 0xFFFFFFFF & (*pMagic);
1871 #if USE_LITTLE_ENDIAN
1872 	if ( uResult==0x01020304 || uResult==0x05060708 )
1873 #else
1874 	if ( uResult==0x08070605 || uResult==0x04030201 )
1875 #endif
1876 		return sErrorMsg;
1877 	return NULL;
1878 }
1879 
1880 //
1881 // $Id$
1882 //
1883