2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://floralicense.org/license/
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an AS IS BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
18 #include "data-cache-debug.h"
20 #ifdef DATA_CACHE_DEBUG
23 #include "../data-cache-io.h"
24 #include "../data-compression.h"
25 #include <dali/integration-api/debug.h>
28 #include <boost/thread.hpp>
42 const char * const DALI_USER_FONT_CACHE_PATH( DALI_USER_FONT_CACHE_DIR );
43 const std::string TEST_FILE("test-file");
45 const unsigned int NUMBER_TEST_ITERATIONS(25000); ///< number of test iterations to perform per thread
46 const bool RANDOM_DATA_SIZE(true); ///< whether to use random data sizes (up to DATA_SIZE)
47 const unsigned int MAX_KEY_VALUE(200000); ////< number key index value
48 const unsigned int DATA_SIZE(64); ///< maximum data size
49 const unsigned int MAX_NUMBER_ENTRIES( 200000 ); ///< Maximum number of entries size
50 const unsigned int ENTRIES_TO_READ_WRITE(1); ///< maximum number of entries to read/write in one API call
51 const Platform::DataCache::CompressionMode COMPRESSION_MODE( Platform::DataCache::RUN_LENGTH_ENCODING); //or COMPRESSION_OFF );
54 * Fills the key vector with random keys between 0 and MAX_KEY_VALUE
56 void FillVectorWithRandomKeys( Platform::DataCache::KeyVector& keyVector)
58 // make sure if we are setup to write 20 key/data pairs in a single API call
59 // the we have at least 20 unique keys
60 DALI_ASSERT_ALWAYS( MAX_KEY_VALUE > ENTRIES_TO_READ_WRITE);
62 // create a set of unique keys, then insert them in to the key vector
63 typedef std::set< Platform::DataCache::DataKey > KeySet;
65 while( uniqueKeys.size() != keyVector.size() )
67 uniqueKeys.insert( rand() % MAX_KEY_VALUE );
70 for( KeySet::const_iterator iter = uniqueKeys.begin(), endIter = uniqueKeys.end(); iter != endIter; ++iter )
72 keyVector[i] = (*iter);
78 * Fill the data vector with random data
80 void FillVectorWithRandomData( Platform::DataCache::DataVector& dataVector)
82 for( unsigned int i = 0; i < dataVector.size(); i++ )
84 unsigned int length( DATA_SIZE );
85 if( RANDOM_DATA_SIZE )
87 length = 1 + rand() % DATA_SIZE;
89 unsigned char* data = new unsigned char[length];
90 dataVector[i].SetData( data, length );
95 * delete the data vector, and check the exists flag is correct
97 void DeleteData(Platform::DataCache::DataVector& dataVector )
99 for( unsigned int i = 0; i < dataVector.size(); i++ )
101 Platform::DataCache::Data& data( dataVector[i] );
102 // check the exists flag
103 if (data.data && !data.exists)
105 DALI_ASSERT_ALWAYS(0 && "data exist flag wrong #1 ");
107 if (!data.data && data.exists)
109 DALI_ASSERT_ALWAYS(0 && "data exist flag wrong #2 ");
119 * read the number of entries, and get the entry list
121 unsigned int CheckNumberEntries( FILE* indexFile, KeyMeta** keyMeta )
123 unsigned int numberEntries;
125 bool ok = ReadNumberEntries( indexFile, numberEntries );
128 // ReadEntries will log an error
129 DALI_ASSERT_ALWAYS(0);
131 // Check there is no data after the header, if there are no entries in the index file
132 if( numberEntries == 0 )
134 if( fgetc( indexFile ) != EOF)
136 // data index is corrupt, it contains more data than are recorded in numberEntries field
137 DALI_LOG_ERROR("index file has zero entries, but contains data after header\n");
138 DALI_ASSERT_ALWAYS(0);
143 // allocate an array of ( key | offset ) structures
144 *keyMeta = new DataCacheIo::KeyMeta[ numberEntries ];
146 // read the index table in to memory
147 ok = ReadEntries(indexFile, *keyMeta, 0, numberEntries);
150 // ReadEntries will log an error
151 DALI_ASSERT_ALWAYS(0);
153 // check there's no data at the end of the file
154 if( fgetc( indexFile ) != EOF)
156 // data index is corrupt, it contains more entries than are recorded in numberEntries field
157 DALI_LOG_ERROR("index has more entries than recorded\n");
158 DALI_ASSERT_ALWAYS(0);
160 return numberEntries;
163 unsigned int GetMaxDataSizeOnFile( unsigned int dataSize )
165 if( COMPRESSION_MODE == Platform::DataCache::RUN_LENGTH_ENCODING )
167 return DataCompression::GetMaximumRleCompressedSize( dataSize );
173 * Checks every single entry in the data file.
174 * This is slow, it should not be performed unless debugging.
176 void FullFileCheck( FILE* indexFile,
178 unsigned int maxDataSize)
180 // Read entries from index file and make sure the file size is correct
181 DataCacheIo::KeyMeta* keyMeta(NULL);
182 unsigned int numberEntries = CheckNumberEntries( indexFile, &keyMeta );
183 unsigned int maxDataSizeOnFile = GetMaxDataSizeOnFile( maxDataSize );
185 // Allocate the file buffer for reading the data, and a decode buffer if the data is compressed
186 unsigned char* fileDataBuffer = new unsigned char[ maxDataSizeOnFile ];
187 unsigned char* decodeBuffer = new unsigned char[ maxDataSize ];
189 // For each entry, check the data is valid in the
190 unsigned int previousOffset = GetHeaderSize();
192 std::set< Dali::Platform::DataCache::DataKey > KeyLookup;
194 for(unsigned int i = 0; i < numberEntries; ++i )
196 Dali::Platform::DataCache::DataKey key( keyMeta[i].mKey );
197 Dali::Platform::DataCache::Data data;
199 // check for duplicate keys
200 if( KeyLookup.find(key) != KeyLookup.end() )
202 printf(" Duplicate Key Found %d \n",key);
203 DALI_ASSERT_ALWAYS(0 );
205 KeyLookup.insert(key);
207 unsigned int offset( keyMeta[i].mOffset);
209 // ensure the offset of each entry is correct
210 DALI_ASSERT_ALWAYS( previousOffset == offset);
212 bool ok = ReadData( dataFile, offset, key, data, fileDataBuffer, maxDataSizeOnFile );
215 DALI_ASSERT_ALWAYS(0 && "DataCacheIO::ReadData failed");
218 unsigned int dataLengthOnFile = data.length;
220 if( COMPRESSION_MODE == Platform::DataCache::RUN_LENGTH_ENCODING )
222 // try and de-compress it
223 std::size_t decodedSize;
224 bool ok = DataCompression::DecodeRle( fileDataBuffer, data.length, decodeBuffer, maxDataSize, decodedSize);
225 DALI_ASSERT_ALWAYS( ok && " DataCompression::DecodeRle failed");
226 data.length = decodedSize;
229 // un-comment for list of key / data entries
230 // printf(" key = %d, length = %d \n",key,data.length);
231 previousOffset = offset + dataLengthOnFile + 8; /* 8 = key + length field */
234 delete []decodeBuffer;
235 delete []fileDataBuffer;
238 void ClearTestFiles( const std::string& indexFileName, const std::string& dataFileName )
240 FILE* dataFile = DataCacheIo::OpenFile( dataFileName, DataCacheIo::DATA_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_WRITE, DataCacheIo::CREATE_IF_MISSING );
241 FILE* indexFile = DataCacheIo::OpenFile( indexFileName, DataCacheIo::INDEX_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_WRITE, DataCacheIo::CREATE_IF_MISSING);
243 ReCreateFiles( indexFile, dataFile, COMPRESSION_MODE);
249 } // unnamed name space
251 void DataCacheStressTest(void)
253 printf("thread started \n");
255 static unsigned int apiCalls(0);
256 std::string path(DALI_USER_FONT_CACHE_PATH);
257 std::string file = path + TEST_FILE ;
259 Platform::DataCache* cache = Platform::DataCache::New( Platform::DataCache::READ_WRITE,
266 for(unsigned int i = 0; i < NUMBER_TEST_ITERATIONS; ++i )
271 printf("DataCache Add() & Find() calls: %d \r",apiCalls);
274 // Read a random array of key/data pairs
275 Platform::DataCache::KeyVector keyVector;
276 Platform::DataCache::DataVector dataVector;
278 unsigned int arraySize = ENTRIES_TO_READ_WRITE;
279 keyVector.resize( arraySize );
281 // read a random entry
282 FillVectorWithRandomKeys( keyVector );
283 cache->Find( keyVector, dataVector);
284 DeleteData( dataVector);
286 // Write a random entry
287 FillVectorWithRandomKeys( keyVector );
288 FillVectorWithRandomData( dataVector );
289 cache->Add( keyVector, dataVector );
290 DeleteData(dataVector);
295 void ThreadedStressTest()
297 // only allow test to run once
298 static int done(false);
305 std::string path(DALI_USER_FONT_CACHE_PATH);
306 std::string dataFileName = path + TEST_FILE + ".data";
307 std::string indexFileName = path + TEST_FILE + ".index";
309 // Make sure the data files are empty to start with
310 ClearTestFiles( indexFileName , dataFileName);
313 printf("____ DataCache Multi Thread Test Starting ____ \n");
315 boost::thread t1(DataCacheStressTest);
316 boost::thread t2(DataCacheStressTest);
317 boost::thread t3(DataCacheStressTest);
318 boost::thread t4(DataCacheStressTest);
319 boost::thread t5(DataCacheStressTest);
320 boost::thread t6(DataCacheStressTest);
321 boost::thread t7(DataCacheStressTest);
322 boost::thread t8(DataCacheStressTest);
333 // Check the data that was written is not corrupt
335 FILE* dataFile = OpenFile( dataFileName, DataCacheIo::DATA_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_ONLY );
336 FILE* indexFile = OpenFile( indexFileName, DataCacheIo::INDEX_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_ONLY );
338 FullFileCheck( indexFile, dataFile, DATA_SIZE );
343 printf("____ DataCache Multi Thread Test PASSED ____ \n");
346 } // namespace DataCacheIO
348 } // namespace SlpPlatform
351 #endif // #ifdef DATA_CACHE_DEBUG