2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "data-cache-debug.h"
21 #ifdef DATA_CACHE_DEBUG
24 #include "../data-cache-io.h"
25 #include "../data-compression.h"
26 #include <dali/integration-api/debug.h>
29 #include <boost/thread.hpp>
43 const char * const DALI_USER_FONT_CACHE_PATH( DALI_USER_FONT_CACHE_DIR );
44 const std::string TEST_FILE("test-file");
46 const unsigned int NUMBER_TEST_ITERATIONS(25000); ///< number of test iterations to perform per thread
47 const bool RANDOM_DATA_SIZE(true); ///< whether to use random data sizes (up to DATA_SIZE)
48 const unsigned int MAX_KEY_VALUE(200000); ////< number key index value
49 const unsigned int DATA_SIZE(64); ///< maximum data size
50 const unsigned int MAX_NUMBER_ENTRIES( 200000 ); ///< Maximum number of entries size
51 const unsigned int ENTRIES_TO_READ_WRITE(1); ///< maximum number of entries to read/write in one API call
52 const Platform::DataCache::CompressionMode COMPRESSION_MODE( Platform::DataCache::RUN_LENGTH_ENCODING); //or COMPRESSION_OFF );
55 * Fills the key vector with random keys between 0 and MAX_KEY_VALUE
57 void FillVectorWithRandomKeys( Platform::DataCache::KeyVector& keyVector)
59 // make sure if we are setup to write 20 key/data pairs in a single API call
60 // the we have at least 20 unique keys
61 DALI_ASSERT_ALWAYS( MAX_KEY_VALUE > ENTRIES_TO_READ_WRITE);
63 // create a set of unique keys, then insert them in to the key vector
64 typedef std::set< Platform::DataCache::DataKey > KeySet;
66 while( uniqueKeys.size() != keyVector.size() )
68 uniqueKeys.insert( rand() % MAX_KEY_VALUE );
71 for( KeySet::const_iterator iter = uniqueKeys.begin(), endIter = uniqueKeys.end(); iter != endIter; ++iter )
73 keyVector[i] = (*iter);
79 * Fill the data vector with random data
81 void FillVectorWithRandomData( Platform::DataCache::DataVector& dataVector)
83 for( unsigned int i = 0; i < dataVector.size(); i++ )
85 unsigned int length( DATA_SIZE );
86 if( RANDOM_DATA_SIZE )
88 length = 1 + rand() % DATA_SIZE;
90 unsigned char* data = new unsigned char[length];
91 dataVector[i].SetData( data, length );
96 * delete the data vector, and check the exists flag is correct
98 void DeleteData(Platform::DataCache::DataVector& dataVector )
100 for( unsigned int i = 0; i < dataVector.size(); i++ )
102 Platform::DataCache::Data& data( dataVector[i] );
103 // check the exists flag
104 if (data.data && !data.exists)
106 DALI_ASSERT_ALWAYS(0 && "data exist flag wrong #1 ");
108 if (!data.data && data.exists)
110 DALI_ASSERT_ALWAYS(0 && "data exist flag wrong #2 ");
120 * read the number of entries, and get the entry list
122 unsigned int CheckNumberEntries( FILE* indexFile, KeyMeta** keyMeta )
124 unsigned int numberEntries;
126 bool ok = ReadNumberEntries( indexFile, numberEntries );
129 // ReadEntries will log an error
130 DALI_ASSERT_ALWAYS(0);
132 // Check there is no data after the header, if there are no entries in the index file
133 if( numberEntries == 0 )
135 if( fgetc( indexFile ) != EOF)
137 // data index is corrupt, it contains more data than are recorded in numberEntries field
138 DALI_LOG_ERROR("index file has zero entries, but contains data after header\n");
139 DALI_ASSERT_ALWAYS(0);
144 // allocate an array of ( key | offset ) structures
145 *keyMeta = new DataCacheIo::KeyMeta[ numberEntries ];
147 // read the index table in to memory
148 ok = ReadEntries(indexFile, *keyMeta, 0, numberEntries);
151 // ReadEntries will log an error
152 DALI_ASSERT_ALWAYS(0);
154 // check there's no data at the end of the file
155 if( fgetc( indexFile ) != EOF)
157 // data index is corrupt, it contains more entries than are recorded in numberEntries field
158 DALI_LOG_ERROR("index has more entries than recorded\n");
159 DALI_ASSERT_ALWAYS(0);
161 return numberEntries;
164 unsigned int GetMaxDataSizeOnFile( unsigned int dataSize )
166 if( COMPRESSION_MODE == Platform::DataCache::RUN_LENGTH_ENCODING )
168 return DataCompression::GetMaximumRleCompressedSize( dataSize );
174 * Checks every single entry in the data file.
175 * This is slow, it should not be performed unless debugging.
177 void FullFileCheck( FILE* indexFile,
179 unsigned int maxDataSize)
181 // Read entries from index file and make sure the file size is correct
182 DataCacheIo::KeyMeta* keyMeta(NULL);
183 unsigned int numberEntries = CheckNumberEntries( indexFile, &keyMeta );
184 unsigned int maxDataSizeOnFile = GetMaxDataSizeOnFile( maxDataSize );
186 // Allocate the file buffer for reading the data, and a decode buffer if the data is compressed
187 unsigned char* fileDataBuffer = new unsigned char[ maxDataSizeOnFile ];
188 unsigned char* decodeBuffer = new unsigned char[ maxDataSize ];
190 // For each entry, check the data is valid in the
191 unsigned int previousOffset = GetHeaderSize();
193 std::set< Dali::Platform::DataCache::DataKey > KeyLookup;
195 for(unsigned int i = 0; i < numberEntries; ++i )
197 Dali::Platform::DataCache::DataKey key( keyMeta[i].mKey );
198 Dali::Platform::DataCache::Data data;
200 // check for duplicate keys
201 if( KeyLookup.find(key) != KeyLookup.end() )
203 printf(" Duplicate Key Found %d \n",key);
204 DALI_ASSERT_ALWAYS(0 );
206 KeyLookup.insert(key);
208 unsigned int offset( keyMeta[i].mOffset);
210 // ensure the offset of each entry is correct
211 DALI_ASSERT_ALWAYS( previousOffset == offset);
213 bool ok = ReadData( dataFile, offset, key, data, fileDataBuffer, maxDataSizeOnFile );
216 DALI_ASSERT_ALWAYS(0 && "DataCacheIO::ReadData failed");
219 unsigned int dataLengthOnFile = data.length;
221 if( COMPRESSION_MODE == Platform::DataCache::RUN_LENGTH_ENCODING )
223 // try and de-compress it
224 std::size_t decodedSize;
225 bool ok = DataCompression::DecodeRle( fileDataBuffer, data.length, decodeBuffer, maxDataSize, decodedSize);
226 DALI_ASSERT_ALWAYS( ok && " DataCompression::DecodeRle failed");
227 data.length = decodedSize;
230 // un-comment for list of key / data entries
231 // printf(" key = %d, length = %d \n",key,data.length);
232 previousOffset = offset + dataLengthOnFile + 8; /* 8 = key + length field */
235 delete []decodeBuffer;
236 delete []fileDataBuffer;
239 void ClearTestFiles( const std::string& indexFileName, const std::string& dataFileName )
241 FILE* dataFile = DataCacheIo::OpenFile( dataFileName, DataCacheIo::DATA_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_WRITE, DataCacheIo::CREATE_IF_MISSING );
242 FILE* indexFile = DataCacheIo::OpenFile( indexFileName, DataCacheIo::INDEX_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_WRITE, DataCacheIo::CREATE_IF_MISSING);
244 ReCreateFiles( indexFile, dataFile, COMPRESSION_MODE);
250 } // unnamed name space
252 void DataCacheStressTest(void)
254 printf("thread started \n");
256 static unsigned int apiCalls(0);
257 std::string path(DALI_USER_FONT_CACHE_PATH);
258 std::string file = path + TEST_FILE ;
260 Platform::DataCache* cache = Platform::DataCache::New( Platform::DataCache::READ_WRITE,
267 for(unsigned int i = 0; i < NUMBER_TEST_ITERATIONS; ++i )
272 printf("DataCache Add() & Find() calls: %d \r",apiCalls);
275 // Read a random array of key/data pairs
276 Platform::DataCache::KeyVector keyVector;
277 Platform::DataCache::DataVector dataVector;
279 unsigned int arraySize = ENTRIES_TO_READ_WRITE;
280 keyVector.resize( arraySize );
282 // read a random entry
283 FillVectorWithRandomKeys( keyVector );
284 cache->Find( keyVector, dataVector);
285 DeleteData( dataVector);
287 // Write a random entry
288 FillVectorWithRandomKeys( keyVector );
289 FillVectorWithRandomData( dataVector );
290 cache->Add( keyVector, dataVector );
291 DeleteData(dataVector);
296 void ThreadedStressTest()
298 // only allow test to run once
299 static int done(false);
306 std::string path(DALI_USER_FONT_CACHE_PATH);
307 std::string dataFileName = path + TEST_FILE + ".data";
308 std::string indexFileName = path + TEST_FILE + ".index";
310 // Make sure the data files are empty to start with
311 ClearTestFiles( indexFileName , dataFileName);
314 printf("____ DataCache Multi Thread Test Starting ____ \n");
316 boost::thread t1(DataCacheStressTest);
317 boost::thread t2(DataCacheStressTest);
318 boost::thread t3(DataCacheStressTest);
319 boost::thread t4(DataCacheStressTest);
320 boost::thread t5(DataCacheStressTest);
321 boost::thread t6(DataCacheStressTest);
322 boost::thread t7(DataCacheStressTest);
323 boost::thread t8(DataCacheStressTest);
334 // Check the data that was written is not corrupt
336 FILE* dataFile = OpenFile( dataFileName, DataCacheIo::DATA_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_ONLY );
337 FILE* indexFile = OpenFile( indexFileName, DataCacheIo::INDEX_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_ONLY );
339 FullFileCheck( indexFile, dataFile, DATA_SIZE );
344 printf("____ DataCache Multi Thread Test PASSED ____ \n");
347 } // namespace DataCacheIO
349 } // namespace SlpPlatform
352 #endif // #ifdef DATA_CACHE_DEBUG