环形队列,支持数据存储和读取,自定义二分查找

  1 #pragma once
  2 #include <stdio.h>
  3 #include <mutex>
  4 #include <condition_variable>
  5 #include <iostream>
  6 #include <chrono>
  7 #include <thread>
  8 using namespace std;
  9 
 10 #define CHECK_STATE(x)    \
 11 if (x == false)            \
 12 {                        \
 13     return false;        \
 14 }                        \
 15 
 16 #pragma pack(push,1)
 17 typedef struct tagCanDate
 18 {
 19     unsigned int m_time;
 20     unsigned short m_speed;
 21     unsigned int m_mileage;
 22 }CanData,*LPCanData;
 23 #pragma pack(pop)
 24 
 25 typedef enum QueueProperty
 26 {
 27     /* if the queue is full, the new frame will override the first*/
 28     eQueueOverride,
 29     /* if the queue is full, the new frame will be discarded*/
 30     eQueueDrop
 31 }QueueProperty;
 32 
 33 
 34 template<class _Ty = void>
 35 struct compare
 36 {
 37     // functor for construct
 38     compare(_Ty t)
 39     {
 40         m_data = t;
 41     }
 42     _Ty m_data;
 43     std::function<int(_Ty, _Ty)> m_fn;
 44 
 45     int operator()(const _Ty& obj) const
 46     {
 47         if (m_fn == NULL)
 48         {
 49             return -1;
 50         }
 51         return m_fn(m_data, obj);
 52     }
 53 };
 54 
 55 template<class T>
 56 class TQueue
 57 {
 58 public:
 59     TQueue();
 60     ~TQueue() = default;
 61 public:
 62     bool Initial(unsigned int size, QueueProperty property = eQueueOverride);
 63     void Destroy();
 64     bool WriteData(T data);
 65     bool ReadData(T &data);
 66     bool Reset();
 67     bool Pop(unsigned int count = 1);
 68     bool IsFull();
 69     bool IsEmpty();
 70     unsigned int GetQueueUsedSize();
 71     unsigned int GetQueueSize();
 72     const T* operator[](unsigned int index) const;
 73 
 74     //get the first frame
 75     const T* head() const;
 76 
 77     //get the tail frame
 78     const T* tail() const;
 79     
 80     bool SaveFile(string path);
 81 
 82     bool LoadFile(string path);
 83 
 84     template<class _Pr = compare<T> >
 85     const T* BinarySearch(_Pr _Pred)
 86     {
 87         CHECK_STATE(m_state);
 88         std::unique_lock<Mutex> lock(m_mutex);
 89         unsigned int middle = 0, low = 0, high = m_used -1;
 90         while (low < high)
 91         {
 92             middle = (low + high) / 2;
 93             T *data = &m_frameQ[(m_head + middle) % m_size];
 94             int ret = _Pred(*data);
 95             if (ret == 0){
 96                 return data;
 97             }
 98             else if (ret > 0){
 99                 high = middle;
100             }
101             else{
102                 low = middle + 1;
103             }
104         }
105         return NULL;
106     }
107 private:
108     /* std::mutex is not reentrant, the exception is thrown if we are trying to 
109     lock mutex while the mutex is already owned by calling thread*/
110     typedef std::recursive_mutex Mutex;
111     bool            m_state;
112     unsigned int    m_tail;
113     unsigned int    m_head;
114     unsigned int    m_size;
115     unsigned int    m_used;
116     QueueProperty    m_property;
117     Mutex            m_mutex;
118     T*                m_frameQ;
119 };
120 
121 template<class T>
122 TQueue<T>::TQueue()
123 {
124     m_size = 0;
125     m_head = 0;
126     m_tail = 0;
127     m_used = 0;
128     m_state = false;
129     m_frameQ = NULL;
130 }
131 
132 
133 template<class T>
134 const T* TQueue<T>::operator[](unsigned int index) const
135 {
136     if (index >= m_used)
137     {
138         return NULL;
139     }
140     return &(m_frameQ[(m_head + index) % m_size]);
141 }
142 
143 //get the first frame
144 template<class T>
145 const T* TQueue<T>::head() const
146 {
147     if (m_used == 0)
148     {
149         return NULL;
150     }
151     return &(m_frameQ[m_head % m_size]);
152 }
153 
154 //get the tail frame
155 template<class T>
156 const T* TQueue<T>::tail() const
157 {
158     if (m_used == 0)
159     {
160         return NULL;
161     }
162     return &(m_frameQ[(m_head + m_used - 1) % m_size]);
163 }
164 
165 template<class T>
166 bool TQueue<T>::Initial(unsigned int size, QueueProperty property)
167 {
168     CHECK_STATE(!m_state);
169     if (size == 0)
170     {
171         return false;
172     }
173     m_size = size;
174     m_frameQ = new T[m_size];
175     if (m_frameQ == NULL)
176     {
177         return false;
178     }
179     m_state = true;
180     Reset();
181     m_property = property;
182     return true;
183 }
184 
185 template<class T>
186 void TQueue<T>::Destroy()
187 {
188     std::unique_lock<Mutex> lock(m_mutex);
189     Reset();
190     m_state = false;
191     if (!m_frameQ)
192     {
193         delete[] m_frameQ;
194         m_frameQ = NULL;
195     }
196 }
197 
198 template<class T>
199 bool TQueue<T>::WriteData(T data)
200 {
201     CHECK_STATE(m_state);
202     std::unique_lock<Mutex> lock(m_mutex);
203     if (IsFull())
204     {
205         if (m_property == eQueueOverride)
206         {
207             //the incoming frame will override the first if the queue is full
208             m_head = (m_head + 1) % m_size;
209             --m_used;
210         }
211         else
212         {
213             Sleep(1);
214             return false;
215         }
216     }
217     m_frameQ[m_tail] = data;
218     ++m_used;
219     m_tail = (m_tail + 1) % m_size;
220     return true;
221 }
222 
223 template<class T>
224 bool TQueue<T>::ReadData(T &data)
225 {
226     CHECK_STATE(m_state);
227     std::unique_lock<Mutex> lock(m_mutex);
228     if (IsEmpty())
229     {
230         //it will return if the queue is empty
231         Sleep(1);
232         return false;
233     }
234     data = m_frameQ[m_head];
235     --m_used;
236     m_head = (m_head + 1) % m_size;
237     return true;
238 }
239 
240 template<class T>
241 bool TQueue<T>::Reset()
242 {
243     try
244     {
245         CHECK_STATE(m_state);
246         std::unique_lock<Mutex> lock(m_mutex);
247         if (m_frameQ != NULL)
248         {
249             memset(m_frameQ, 0, m_size * sizeof(T));
250         }
251         m_head = 0;
252         m_tail = 0;
253         m_used = 0;
254     }
255     catch (const std::exception &e)
256     {
257         std::cout << e.what() << '\n';
258         throw e;
259     }
260     return true;
261 }
262 
263 
264 template<class T>
265 bool TQueue<T>::Pop(unsigned int count)
266 {
267     CHECK_STATE(m_state);
268     std::unique_lock<Mutex> lock(m_mutex);
269     //calculate how many frame to drop
270     unsigned int popCount = m_used > count ? count : m_used;
271     if (popCount < 1)
272     {
273         //the function returns if the count of pop frames is less than 1
274         Sleep(0);
275         return false;
276     }
277     m_used -= popCount;
278     m_head = (m_head + popCount) % m_size;
279     return true;
280 }
281 
282 template<class T>
283 unsigned int TQueue<T>::GetQueueUsedSize()
284 {
285     return m_used;
286 }
287 
288 template<class T>
289 unsigned int TQueue<T>::GetQueueSize()
290 {
291     return m_size;
292 }
293 
294 template<class T>
295 bool TQueue<T>::IsFull()
296 {
297     return  (m_used == m_size);
298 }
299 
300 template<class T>
301 bool TQueue<T>::IsEmpty()
302 {
303     return (m_used == 0);
304 }
305 
306 template<class T>
307 bool TQueue<T>::SaveFile(string path)
308 {
309     CHECK_STATE(m_state);
310     std::unique_lock<Mutex> lock(m_mutex);
311     if (path.length() == 0)
312     {
313         return false;
314     }
315     bool ret = true;
316     FILE * fd = NULL;
317     fopen_s(&fd, path.c_str(), "wb");
318     if (NULL != fd)
319     {
320         int total_len = 0;
321         int len = 0;
322         int offset = 0;
323         int data_len = sizeof(T);
324         int try_count = 0;
325         for (int i = 0; i < m_used; i++)
326         {
327             const T data = m_frameQ[(m_head + i) % m_size];
328             while (offset != data_len)
329             {
330                 len = fwrite(&data + offset, 1, data_len - offset, fd);
331                 if (len == -1)
332                 {
333                     if (++try_count > 3)
334                     {
335                         ret = false;
336                         break;
337                     }
338                     else
339                     {
340                         printf("SaveFile fwrite err:%d\n", errno);
341                         Sleep(0);
342                         continue;
343                     }
344                 }
345                 offset += len;
346                 total_len += len;
347             }
348             try_count = 0;
349             len = 0;
350             offset = 0;
351         }
352         fflush(fd);
353         fclose(fd);
354     }
355     else
356     {
357         ret = false;
358         printf("SaveFile fopen err:%d\n", errno);
359     }
360     return ret;
361 }
362 
363 template<class T >
364 bool TQueue<T>::LoadFile(string path)
365 {
366     CHECK_STATE(m_state);
367     std::unique_lock<Mutex> lock(m_mutex);
368     if (path.length() == 0)
369     {
370         return false;
371     }
372     bool ret = true;
373     struct stat st;
374     if (::stat(path.c_str(), &st) != 0)
375     {
376         printf("LoadFile file is not exist!\n");
377         return false;
378     }
379     _off_t file_size = st.st_size;
380     FILE * fd = NULL;
381     fopen_s(&fd, path.c_str(), "rb+");
382     if (NULL != fd)
383     {
384         int total_len = 0;
385         int len = 0;
386         int offset = 0;
387         int try_count = 0;
388         int data_len = sizeof(T);
389         T data;
390         Reset();
391         for (; file_size >= total_len + data_len; )
392         {
393             memset(&data, 0, data_len);
394             while (offset != data_len)
395             {
396                 len = fread(&data + offset, 1, data_len - offset, fd);
397                 if (len == -1)
398                 {
399                     if (++try_count > 3)
400                     {
401                         ret = false;
402                         break;
403                     }
404                     else
405                     {
406                         printf("LoadFile fread err:%d\n", errno);
407                         Sleep(0);
408                         continue;
409                     }
410                 }
411                 offset += len;
412                 total_len += len;
413                 if (feof(fd) && offset != data_len)
414                 {
415                     //the function is return when the end indicator of file is set and offset is not equal data_len
416                     printf("LoadFile file is end!\n");
417                     return ret;
418                 }
419             }
420             WriteData(data);
421             try_count = 0;
422             len = 0;
423             offset = 0;
424         }
425         fclose(fd);
426     }
427     else
428     {
429         ret = false;
430     }
431     return ret;
432 }

测试代码

 1 int main()
 2 {
 3     time_t cur = time(NULL);
 4     auto fn_time = [](unsigned int t)->string {
 5         char buff[128] = { '\0' };
 6         time_t now = t;
 7         struct tm  cur;
 8         localtime_s(&cur, &now);
 9         sprintf_s(buff, sizeof(buff), "%04d-%02d-%02d %02d:%02d:%02d", cur.tm_year + 1900, cur.tm_mon + 1, cur.tm_mday, cur.tm_hour, cur.tm_min, cur.tm_sec);
10         return buff;
11     };
12     TQueue<CanData> CanQueue;
13     //3*24*60*60/10
14     CanQueue.Initial(25920);
15     //CanQueue.Initial(10);
16     auto fn_write = [cur](TQueue<CanData> &CanQueue) {
17         printf("fn_write CanQueue address:0x%08x \n", &CanQueue);
18         short index = 0;
19         do
20         {
21             CanData data;
22             data.m_mileage = index;
23             data.m_speed = index;
24             data.m_time = (unsigned int)cur + index * 10;
25             CanQueue.WriteData(data);
26             //printf("write data , mileage:%d, speed:%d, time:%d \n", data.mileage, data.speed, data.time);
27             index++;
28             if (CanQueue.IsFull())
29             {
30                 CanQueue.SaveFile("raw.txt");
31                 break;
32             }
33             //Sleep(50);
34         } while (1);
35     };
36     std::thread QueueWrite(fn_write, std::ref(CanQueue));
37     QueueWrite.join();
38     auto fn_read = [cur, fn_time](TQueue<CanData> *CanQueue) {
39         printf("fn_read CanQueue address:0x%08x \n", CanQueue);
40         do
41         {
42             /*CanData data;
43             const CanData *pData = (*CanQueue)[9];
44             CanQueue->ReadData(data);
45             int result = memcmp(pData, &data, sizeof(pData));
46             printf("read data ,result:%d,count:%d, mileage:%d, speed:%d, time:%d \n", result,CanQueue->GetQueueUsedSize(), data.mileage, data.speed, data.time);
47             */
48             if (CanQueue->IsFull())
49             {
50                 auto t1 = std::chrono::high_resolution_clock::now();
51                 CanQueue->LoadFile("raw.txt");
52                 auto t2 = std::chrono::high_resolution_clock::now();
53                 printf("it cost :%lldms to read data!\n", std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1));
54             }
55             {
56                 //二分法进行数据查找
57                 auto t1 = std::chrono::high_resolution_clock::now();
58                 CanData canData;
59                 canData.m_time = (unsigned int)cur + 6000 * 10;
60                 //自定义数据比较方法
61                 struct compare<CanData> cmp(canData);
62                 cmp.m_fn = [](const CanData &first, const CanData &second)->int {
63                     if (first.m_time + 10 >= second.m_time && first.m_time <= second.m_time) {
64                         return 0;
65                     }
66                     else if (first.m_time + 10 < second.m_time) {
67                         return 1;
68                     }
69                     else if (first.m_time > second.m_time) {
70                         return -1;
71                     }
72                 };
73                 //查找
74                 const CanData *pCanData = CanQueue->BinarySearch(cmp);
75                 if (pCanData != NULL)
76                 {
77                     string t1 = fn_time(pCanData->m_time);
78                     string t2 = fn_time(canData.m_time);
79                     printf("serach time:%s, base time:%s\n", t1.c_str(), t2.c_str());
80                 }
81                 auto t2 = std::chrono::high_resolution_clock::now();
82                 printf("it cost :%lldms to read data!\n", std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1));
83             }
84             break;
85         } while (1);
86     };
87     std::thread QueueRead(fn_read, &CanQueue);
88     QueueRead.join();
89     system("Pause");
90 }

 

posted on 2020-09-22 17:43  吃橙子  阅读(650)  评论(0编辑  收藏  举报

导航