xref: /f-stack/app/micro_thread/mt_cache.cpp (revision 144c6bcd)
1 
2 /**
3  * Tencent is pleased to support the open source community by making MSEC available.
4  *
5  * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
6  *
7  * Licensed under the GNU General Public License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License. You may
9  * obtain a copy of the License at
10  *
11  *     https://opensource.org/licenses/GPL-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software distributed under the
14  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
15  * either express or implied. See the License for the specific language governing permissions
16  * and limitations under the License.
17  */
18 
19 
20 /**
21  *  @filename mt_cache.cpp
22  *  @info   TCP����buffer����ʵ��
23  */
24 
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <string.h>
30 #include "mt_incl.h"
31 #include "kqueue_proxy.h"
32 #include "micro_thread.h"
33 #include "mt_sys_hook.h"
34 #include "ff_hook.h"
35 
36 #include "mt_cache.h"
37 
38 namespace NS_MICRO_THREAD {
39 
40 
41 /**
42  * @brief  Buffer��������
43  * @param  size ʵ�ʵ�buff��С
44  * @return ��NULL block��, ����ʧ��
45  */
46 TSkBuffer* new_sk_buffer(uint32_t size)
47 {
48     uint32_t total = sizeof(TSkBuffer) + size;
49     total = (total + SK_DFLT_ALIGN_SIZE - 1) / SK_DFLT_ALIGN_SIZE * SK_DFLT_ALIGN_SIZE;
50     TSkBuffer* block = (TSkBuffer*)malloc(total);
51     if (block == NULL)
52     {
53         MTLOG_ERROR("malloc failed, no more memory[%u]", total);
54         return NULL;
55     }
56 
57     block->last_time = 0;
58     block->size = size;
59     block->head = block->buff;
60     block->end  = block->buff + size;
61 
62     block->data = block->head;
63     block->data_len = 0;
64 
65     return block;
66 }
67 
68 
69 /**
70  * @brief  Buffer�ͷŲ���
71  * @param  block -buff��
72  */
73 void delete_sk_buffer(TSkBuffer* block)
74 {
75     if (NULL == block) {
76         return;
77     }
78 
79     free(block);
80 }
81 
82 
83 /**
84  * @brief ������������Ϣ(����Դ�ػ���buff,����չ)
85  * @param buff -���е�buffָ��
86  * @param size -��Ҫ��չ�����ճ��ȴ�С
87  * @return ʵ�ʵ�buff��Ϣ
88  */
89 TSkBuffer* reserve_sk_buffer(TSkBuffer* buff, uint32_t size)
90 {
91     if (NULL == buff) {
92         return new_sk_buffer(size);
93     }
94 
95     if (buff->size >= size) {
96         return buff;
97     }
98 
99     TSkBuffer* new_buff = new_sk_buffer(size);
100     if (NULL == new_buff) {
101         return buff;
102     }
103     memcpy(new_buff->data, buff->data, buff->data_len);
104     new_buff->data_len = buff->data_len;
105     delete_sk_buffer(buff);
106 
107     return new_buff;
108 }
109 
110 
111 /**
112  * @brief  cache �صij�ʼ���ӿ�
113  * @param  mng -����ص�ָ��
114  * @param  expired -�����ʱ��, ��λ��
115  * @param  size -�������Ĭ�����ɵĿ��С
116  */
117 void sk_buffer_mng_init(TSkBuffMng* mng, uint32_t expired, uint32_t size)
118 {
119     TAILQ_INIT(&mng->free_list);
120     mng->expired  = expired;
121     mng->count = 0;
122     mng->size = size;
123 }
124 
125 /**
126  * @brief  cache �ص����ٽӿ�
127  * @param  mng -����ص�ָ��
128  */
129 void sk_buffer_mng_destroy(TSkBuffMng * mng)
130 {
131     TSkBuffer* item = NULL;
132     TSkBuffer* tmp = NULL;
133     TAILQ_FOREACH_SAFE(item, &mng->free_list, entry, tmp)
134     {
135         TAILQ_REMOVE(&mng->free_list, item, entry);
136         delete_sk_buffer(item);
137     }
138     mng->count = 0;
139 }
140 
141 
142 /**
143  * @brief  �������һ��buff
144  * @param  mng -����ص�ָ��
145  * @return ��NULLΪ�ɹ���ȡ��buff��ָ��
146  */
147 TSkBuffer* alloc_sk_buffer(TSkBuffMng* mng)
148 {
149     if (NULL == mng) {
150         return NULL;
151     }
152 
153     TSkBuffer* item = TAILQ_FIRST(&mng->free_list);
154     if (item != NULL)
155     {
156         TAILQ_REMOVE(&mng->free_list, item, entry);
157         mng->count--;
158         return item;
159     }
160 
161     item = new_sk_buffer(mng->size);
162     if (NULL == item)
163     {
164         return NULL;
165     }
166 
167     return item;
168 }
169 
170 
171 /**
172  * @brief �ͷ�ָ����buff��
173  * @param  mng -����ص�ָ��
174  * @param  buff -���ͷŵ�buffָ��
175  */
176 void free_sk_buffer(TSkBuffMng* mng, TSkBuffer* buff)
177 {
178     if ((NULL == mng) || (NULL == buff)) {
179         return;
180     }
181 
182     TAILQ_INSERT_TAIL(&mng->free_list, buff, entry);
183     mng->count++;
184 
185     buff->last_time = (uint32_t)(mt_time_ms() / 1000);
186     buff->data = buff->head;
187     buff->data_len = 0;
188 }
189 
190 
191 /**
192  * @brief ���չ��ڵ�buff��
193  * @param  mng -����ص�ָ��
194  * @param  now -��ǰ��ʱ��, �뼶��
195  */
196 void recycle_sk_buffer(TSkBuffMng* mng, uint32_t now)
197 {
198     TSkBuffer* item = NULL;
199     TSkBuffer* tmp = NULL;
200     TAILQ_FOREACH_SAFE(item, &mng->free_list, entry, tmp)
201     {
202         if ((now - item->last_time) < mng->expired)
203         {
204             break;
205         }
206 
207         TAILQ_REMOVE(&mng->free_list, item, entry);
208         delete_sk_buffer(item);
209         mng->count--;
210     }
211 }
212 
213 
214 /**
215  * @brief Cache��������ʼ��
216  * @param cache -�����ָ��
217  * @param pool -buff��ָ��
218  */
219 void rw_cache_init(TRWCache* cache, TSkBuffMng* pool)
220 {
221     TAILQ_INIT(&cache->list);
222     cache->len = 0;
223     cache->count = 0;
224     cache->pool = pool;
225 }
226 
227 /**
228  * @brief Cache����������
229  * @param cache -�����ָ��
230  */
231 void rw_cache_destroy(TRWCache* cache)
232 {
233     if ((cache == NULL) || (cache->pool == NULL)) {
234         return;
235     }
236 
237     TSkBuffer* item = NULL;
238     TSkBuffer* tmp = NULL;
239     TAILQ_FOREACH_SAFE(item, &cache->list, entry, tmp)
240     {
241         TAILQ_REMOVE(&cache->list, item, entry);
242         free_sk_buffer(cache->pool, item);
243     }
244     cache->count = 0;
245     cache->len = 0;
246     cache->pool = NULL;
247 }
248 
249 
250 /**
251  * @brief Cacheɾ��������ָ����������
252  * @param cache -�����ָ��
253  * @param buff -���buff��ָ��
254  * @param len -��ɾ���ij���
255  * @return ʵ�ʿ�������
256  */
257 uint32_t cache_copy_out(TRWCache* cache, void* buff, uint32_t len)
258 {
259     if ((cache == NULL) || (cache->pool == NULL)) {
260         return 0;
261     }
262 
263     char* out_buff = (char*)buff;
264     uint32_t left = len, skip_len = 0;
265     TSkBuffer* item = NULL;
266     TSkBuffer* tmp = NULL;
267     TAILQ_FOREACH_SAFE(item, &cache->list, entry, tmp)
268     {
269         // 1. ȷ�Ͽ������ݴ�С
270         skip_len = (item->data_len > left) ? left : item->data_len;
271         if (out_buff != NULL)
272         {
273             memcpy(out_buff, item->data, skip_len);
274             out_buff += skip_len;
275         }
276 
277         left -= skip_len;
278         item->data_len -= skip_len;
279         item->data += skip_len;
280         if (item->data_len > 0)
281         {
282             break;
283         }
284 
285         // 2. �Ƴ�һ��block
286         if (cache->count > 0) {
287             cache->count--;
288         }
289         TAILQ_REMOVE(&cache->list, item, entry);
290         free_sk_buffer(cache->pool, item);
291 
292         // 3. ѭ����������
293         if (left == 0)
294         {
295             break;
296         }
297     }
298 
299     // ���忼�����ݳ�������, �Ƿ����㹻�������Ƴ�
300     skip_len = len - left;
301     if (cache->len > skip_len)
302     {
303         cache->len -= skip_len;
304     }
305     else
306     {
307         cache->len = 0;
308     }
309 
310     return skip_len;
311 }
312 
313 
314 /**
315  * @brief Cacheɾ����ָ����������
316  * @param cache -�����ָ��
317  * @param len -��ɾ���ij���
318  */
319 void cache_skip_data(TRWCache* cache, uint32_t len)
320 {
321     cache_copy_out(cache, NULL, len);
322 }
323 
324 /**
325  * @brief Cache׷��ָ����������
326  * @param cache -�����ָ��
327  * @param buff -��׷�ӵĿ�ָ��
328  */
329 void cache_append_buffer(TRWCache* cache, TSkBuffer* buff)
330 {
331     if ((NULL == cache) || (NULL == buff))
332     {
333         return;
334     }
335 
336     TAILQ_INSERT_TAIL(&cache->list, buff, entry);
337     cache->len += buff->data_len;
338     cache->count++;
339 }
340 
341 /**
342  * @brief Cache�Ƴ���һ���ڴ�, ����free
343  * @param cache -�����ָ��
344  */
345 TSkBuffer* cache_skip_first_buffer(TRWCache* cache)
346 {
347     TSkBuffer* buff = TAILQ_FIRST(&cache->list);
348     if ((NULL == cache) || (NULL == buff))
349     {
350         return NULL;
351     }
352 
353     TAILQ_REMOVE(&cache->list, buff, entry);
354     if (cache->len >= buff->data_len)
355     {
356         cache->len -= buff->data_len;
357     }
358 
359     if (cache->count > 0)
360     {
361         cache->count--;
362     }
363 
364     return buff;
365 }
366 
367 
368 /**
369  * @brief Cache׷��ָ����������
370  * @param cache -�����ָ��
371  * @param data -��׷�ӵ�ָ��
372  * @param len -��׷�ӵij���
373  */
374 int32_t cache_append_data(TRWCache* cache, const void* data, uint32_t len)
375 {
376     if ((NULL == data) || (NULL == cache) || (NULL == cache->pool))
377     {
378         return -1;
379     }
380 
381     if (len == 0)
382     {
383         return 0;
384     }
385 
386     uint32_t left = len;
387     uint32_t remain = 0;
388 
389     // 1. β�ռ��Ƚ���append, ��Ϊ��Ҫ�ع�, ǰһ�����Ȳ�����
390     TSkBuffer* tail = TAILQ_LAST(&cache->list, __sk_buff_list);
391     if (tail != NULL)
392     {
393         if (tail->end > (tail->data + tail->data_len))
394         {
395             remain = tail->end - tail->data - tail->data_len;
396         }
397 
398         if (remain >= len)
399         {
400             memcpy(tail->data + tail->data_len, data, len);
401             tail->data_len += len;
402             cache->len += len;
403             return (int32_t)len;
404         }
405     }
406 
407     // 2. ��ʣ��buff������, ������ʣ���buff, ���޸�β�ڵ�
408     TRWCache keep_list;
409     rw_cache_init(&keep_list, cache->pool);
410     left -= remain;
411     while (left > 0)
412     {
413         TSkBuffer* item = alloc_sk_buffer(cache->pool);
414         if (item == NULL)
415         {
416             rw_cache_destroy(&keep_list);
417             return -2;
418         }
419         cache_append_buffer(&keep_list, item);
420 
421         if (left <= item->size)
422         {
423             memcpy(item->head, (char*)data + len - left, left);
424             item->data_len = left;
425             break;
426         }
427 
428         memcpy(item->head, (char*)data + len - left, item->size);
429         item->data_len = item->size;
430         left -= item->size;
431     }
432 
433     // 3. ���Կ��������buff, ���ﲻ��ع���
434     if ((tail != NULL) && (remain > 0))
435     {
436         memcpy(tail->data + tail->data_len, data, remain);
437         tail->data_len += remain;
438     }
439 
440     cache->len += len;
441     cache->count += keep_list.count;
442     TAILQ_CONCAT(&cache->list, &keep_list.list, entry);
443 
444     return (int32_t)len;
445 }
446 
447 
448 /**
449  * @brief Cache���ϵ�UDP�ձ��ӿ�, �����ڴ�Ƚ϶�, ������32λʹ��
450  * @param cache -�����ָ��
451  * @param fd - ׼���ձ���fd���
452  * @param remote_addr -�Զ�ip��ַ
453  * @return ʵ�ʽ��ճ���
454  */
455 int32_t cache_udp_recv(TRWCache* cache, uint32_t fd, struct sockaddr_in* remote_addr)
456 {
457     if (NULL == cache)
458     {
459         return -1;
460     }
461 
462     int32_t total = 0;
463     for (uint32_t i = 0; i < 100; i++)
464     {
465         TSkBuffer* item = alloc_sk_buffer(cache->pool);
466         if (NULL == item)
467         {
468             return -2;
469         }
470 
471         socklen_t addr_len = sizeof(*remote_addr);
472         mt_hook_syscall(recvfrom);
473         int32_t rc = ff_hook_recvfrom(fd, item->data, item->size, 0, (struct sockaddr*)remote_addr, &addr_len);
474         if (rc <= 0)
475         {
476             free_sk_buffer(cache->pool, item);
477 
478             if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
479             {
480                 break;
481             }
482             else
483             {
484                 MTLOG_ERROR("recvfrom failed, fd[%d] ret %d[%m]", fd, rc);
485                 return -3;
486             }
487         }
488 
489         item->data_len += rc;
490         cache_append_buffer(cache, item);
491         total += rc;
492     }
493 
494     return total;
495 }
496 
497 
498 /**
499  * @brief Cache���ϵ�TCP�ձ��ӿ�
500  * @param cache -�����ָ��
501  * @param fd - ׼���ձ���fd���
502  * @return ʵ�ʽ��ճ���
503  */
504 int32_t cache_tcp_recv(TRWCache* cache, uint32_t fd)
505 {
506     if (NULL == cache)
507     {
508         return -1;
509     }
510 
511     int32_t total = 0;
512     for (uint32_t i = 0; i < 100; i++)
513     {
514         // 1. ÿ�μ��β�ռ�, �ռ������ʼ״̬, �����¿ռ�
515         TSkBuffer* item = TAILQ_LAST(&cache->list, __sk_buff_list);
516         if ((NULL == item)
517             || ((item->data_len + item->data) >= item->end))
518         {
519             item = alloc_sk_buffer(cache->pool);
520             if (item == NULL)
521             {
522                return -2;
523             }
524             cache_append_buffer(cache, item);
525         }
526 
527         // 2. ����������size��С, Ĭ��64K
528         uint8_t* buff = item->data + item->data_len;
529         uint32_t remain = item->end - item->data - item->data_len;
530         mt_hook_syscall(recv);
531         int32_t recvd_len = ff_hook_recv(fd, buff, remain, 0);
532         if (recvd_len == 0)
533         {
534             MTLOG_DEBUG("remote close, socket: %d", fd);
535             return -SK_ERR_NEED_CLOSE;
536         }
537         else if (recvd_len < 0)
538         {
539             if (errno == EAGAIN)
540             {
541                 return total;
542             }
543             else
544             {
545                 MTLOG_ERROR("recv tcp socket failed, error: %d[%m]", errno);
546                 return -2;
547             }
548         }
549         else
550         {
551             item->data_len += recvd_len;
552             cache->len += recvd_len;
553             total += recvd_len;
554             if (recvd_len < (int32_t)remain) // �ղ���, ����Ϊ����OK
555             {
556                 return total;
557             }
558         }
559     }
560 
561     return total;
562 }
563 
564 /**
565  * @brief Cache���ϵ�TCP���ͽӿ�
566  * @param cache -�����ָ��
567  * @param fd - ׼��������fd���
568  * @return ʵ�ʷ��ͳ���
569  */
570 int32_t cache_tcp_send(TRWCache* cache, uint32_t fd)
571 {
572     if ((NULL == cache) || (NULL == cache->pool))
573     {
574         return -1;
575     }
576 
577     if (cache->len == 0)
578     {
579         return 0;
580     }
581 
582 
583     int32_t ret = 0, total = 0;
584     TSkBuffer* item = NULL;
585     TSkBuffer* tmp = NULL;
586     TAILQ_FOREACH_SAFE(item, &cache->list, entry, tmp)
587     {
588         mt_hook_syscall(send);
589         ret = ff_hook_send(fd, item->data, item->data_len, 0);
590         if (ret < 0)
591         {
592             break;
593         }
594 
595         total += ret;
596         if (ret < (int32_t)item->data_len)
597         {
598             break;
599         }
600     }
601 
602     cache_skip_data(cache, total);
603     if (ret < 0)
604     {
605         if (errno != EAGAIN)
606         {
607             MTLOG_ERROR("tcp socket send failed, error: %d[%m]", errno);
608             return -2;
609         }
610     }
611 
612     return total;
613 }
614 
615 
616 /**
617  * @brief Cache���ϵ�TCP���ͽӿ�, δʹ��IOVEC
618  * @param cache -�����ָ��
619  * @param fd - ׼��������fd���
620  * @param data -������cache��, �������͵�buff
621  * @param len  -�������͵�buff����
622  * @return ʵ�ʷ��ͳ���
623  */
624 int32_t cache_tcp_send_buff(TRWCache* cache, uint32_t fd, const void* data, uint32_t len)
625 {
626     if ((NULL == cache) || (NULL == data))
627     {
628         return -1;
629     }
630 
631     // 1. ���ȷ���CACHE����
632     int32_t ret = cache_tcp_send(cache, fd);
633     if (ret < 0)
634     {
635         MTLOG_ERROR("tcp socket[%d] send cache data failed, rc: %d", fd, ret);
636         return ret;
637     }
638 
639     // 2. CACHE�Ѿ�������
640     int32_t send_len = 0;
641     if (cache->len == 0)
642     {
643         mt_hook_syscall(send);
644         ret = ff_hook_send(fd, data, len, 0);
645         if (ret >= 0)
646         {
647             send_len += ret;
648         }
649         else
650         {
651             if (errno != EAGAIN)
652             {
653                 MTLOG_ERROR("tcp socket[%d] send failed, error: %d[%m]", fd, errno);
654                 return -2;
655             }
656         }
657     }
658 
659     int32_t rc = cache_append_data(cache, (char*)data + send_len, len - send_len);
660     if (rc < 0)
661     {
662         MTLOG_ERROR("tcp socket[%d] apend data failed, rc: %d", fd, rc);
663         return -3;
664     }
665 
666     return send_len;
667 }
668 
669 
670 /**
671  * @brief ��ȡcache��Ч�����ܳ���
672  * @param multi -�����ָ��
673  * @return ʵ����Ч���ݳ���
674  */
675 uint32_t get_data_len(TBuffVecPtr multi)
676 {
677     TRWCache* cache = (TRWCache*)multi;
678     if (NULL == cache) {
679         return 0;
680     } else {
681         return cache->len;
682     }
683 }
684 
685 /**
686  * @brief ��ȡcache��Ч���ݿ����
687  * @param multi -�����ָ��
688  * @return ʵ����Ч���ݿ����
689  */
690 uint32_t get_block_count(TBuffVecPtr multi)
691 {
692     TRWCache* cache = (TRWCache*)multi;
693     if (NULL == cache) {
694         return 0;
695     } else {
696         return cache->count;
697     }
698 }
699 
700 /**
701  * @brief ��ȡcache�ĵ�һ������ָ��
702  * @param multi -�����ָ��
703  * @return ��һ������ָ��
704  */
705 TBuffBlockPtr get_first_block(TBuffVecPtr multi)
706 {
707     TRWCache* cache = (TRWCache*)multi;
708     if (NULL == cache) {
709         return NULL;
710     } else {
711         return (TBuffBlockPtr)TAILQ_FIRST(&cache->list);
712     }
713 }
714 
715 /**
716  * @brief ��ȡcache����һ������ָ��
717  * @param multi -�����ָ��
718  * @param block -��ǰ��ָ��
719  * @return ��һ������ָ��
720  */
721 TBuffBlockPtr get_next_block(TBuffVecPtr multi, TBuffBlockPtr block)
722 {
723     TRWCache* cache = (TRWCache*)multi;
724     TSkBuffer* item = (TSkBuffer*)block;
725     if ((NULL == cache) || (NULL == item))
726     {
727         return NULL;
728     }
729 
730     return (TBuffBlockPtr)TAILQ_NEXT(item, entry);
731 
732 }
733 
734 /**
735  * @brief ��ȡ���ݿ��ָ�������ݳ���
736  * @param block -��ǰ��ָ��
737  * @param data -����ָ��-modify����
738  * @param len  -����ָ�� modify����
739  */
740 void get_block_data(TBuffBlockPtr block, const void** data, int32_t* len)
741 {
742     TSkBuffer* item = (TSkBuffer*)block;
743     if (NULL == block)
744     {
745         return;
746     }
747 
748     if (data != NULL)
749     {
750         *(uint8_t**)data = item->data;
751     }
752 
753     if (len != NULL)
754     {
755         *len = (int32_t)item->data_len;
756     }
757 }
758 
759 
760 /**
761  * @brief ��ȡ���ݿ��ָ�������ݳ���
762  * @param multi -�����ָ��
763  * @param data -����д������ָ��
764  * @param len  -����
765  * @return ���ݶ�ȡ�����ݳ���
766  */
767 uint32_t read_cache_data(TBuffVecPtr multi, void* data, uint32_t len)
768 {
769     TRWCache* cache = (TRWCache*)multi;
770     if (NULL == cache) {
771         return 0;
772     }
773 
774     uint32_t left_len = len;
775     uint32_t offset = 0;
776     TSkBuffer* item = NULL;
777     TSkBuffer* tmp = NULL;
778     TAILQ_FOREACH_SAFE(item, &cache->list, entry, tmp)
779     {
780         uint32_t copy_len = 0;
781         if (left_len <= item->data_len)
782         {
783             copy_len = left_len;
784         }
785         else
786         {
787             copy_len = item->data_len;
788         }
789 
790         if (data != NULL)
791         {
792             memcpy((char*)data + offset, item->data, copy_len);
793         }
794         offset += copy_len;
795         left_len -= copy_len;
796 
797         if (left_len <= 0)
798         {
799             break;
800         }
801     }
802 
803     return offset;
804 }
805 
806 
807 
808 /**
809  * @brief ��ȡ���ݿ��ָ�������ݳ���
810  * @param multi -�����ָ��
811  * @param data -����д������ָ��
812  * @param len  -����
813  * @return ���ݶ�ȡ�����ݳ���
814  */
815 uint32_t read_cache_begin(TBuffVecPtr multi, uint32_t begin, void* data, uint32_t len)
816 {
817     TRWCache* cache = (TRWCache*)multi;
818     if (NULL == cache) {
819         return 0;
820     }
821 
822     if (begin >= cache->len) {
823         return 0;
824     }
825 
826     uint32_t pos_left = begin;
827     uint32_t copy_left = len;
828     uint32_t offset = 0;
829     TSkBuffer* item = NULL;
830     TAILQ_FOREACH(item, &cache->list, entry)
831     {
832         // 1. ��ʼλ����ʣ��, �������ò���
833         uint8_t* start_ptr = item->data;
834         uint32_t real_left = item->data_len;
835         if (pos_left > 0)
836         {
837             uint32_t skip_len = pos_left > real_left ? real_left : pos_left;
838             pos_left -= skip_len;
839             real_left -= skip_len;
840             start_ptr += skip_len;
841         }
842 
843         // 2. �������޳���ʣ��, ��ȴ���һ��
844         if (real_left == 0)
845         {
846             continue;
847         }
848 
849         // 3. ��ʣ��, �������������
850         uint32_t copy_len = copy_left > real_left ? real_left : copy_left;
851         if (data != NULL)
852         {
853             memcpy((char*)data + offset, start_ptr, copy_len);
854         }
855         offset += copy_len;
856         copy_left -= copy_len;
857         if (copy_left == 0)
858         {
859             break;
860         }
861     }
862 
863     return offset;
864 }
865 
866 
867 
868 };
869 
870