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