00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050 #include <config.h>
00051
00052 #include <drizzled/internal/my_sys.h>
00053 #include <drizzled/internal/m_string.h>
00054 #include <drizzled/drizzled.h>
00055 #ifdef HAVE_AIOWAIT
00056 #include <drizzled/error.h>
00057 #include <drizzled/internal/aio_result.h>
00058 static void my_aiowait(my_aio_result *result);
00059 #endif
00060 #include <drizzled/internal/iocache.h>
00061 #include <errno.h>
00062 #include <drizzled/util/test.h>
00063 #include <stdlib.h>
00064 #include <algorithm>
00065
00066 using namespace std;
00067
00068 namespace drizzled
00069 {
00070 namespace internal
00071 {
00072
00073 static int _my_b_read(st_io_cache *info, unsigned char *Buffer, size_t Count);
00074 static int _my_b_write(st_io_cache *info, const unsigned char *Buffer, size_t Count);
00075
00080 inline
00081 static void lock_append_buffer(st_io_cache *, int )
00082 {
00083 }
00084
00089 inline
00090 static void unlock_append_buffer(st_io_cache *, int )
00091 {
00092 }
00093
00098 inline
00099 static size_t io_round_up(size_t x)
00100 {
00101 return ((x+IO_SIZE-1) & ~(IO_SIZE-1));
00102 }
00103
00108 inline
00109 static size_t io_round_dn(size_t x)
00110 {
00111 return (x & ~(IO_SIZE-1));
00112 }
00113
00114
00125 void st_io_cache::setup_io_cache()
00126 {
00127
00128 if (type == WRITE_CACHE)
00129 {
00130 current_pos= &write_pos;
00131 current_end= &write_end;
00132 }
00133 else
00134 {
00135 current_pos= &read_pos;
00136 current_end= &read_end;
00137 }
00138 }
00139
00140
00141 void st_io_cache::init_functions()
00142 {
00143 switch (type) {
00144 case READ_NET:
00145
00146
00147
00148
00149
00150
00151
00152 break;
00153 default:
00154 read_function = _my_b_read;
00155 write_function = _my_b_write;
00156 }
00157
00158 setup_io_cache();
00159 }
00160
00178 int st_io_cache::init_io_cache(int file_arg, size_t cachesize,
00179 enum cache_type type_arg, my_off_t seek_offset,
00180 bool use_async_io, myf cache_myflags)
00181 {
00182 size_t min_cache;
00183 off_t pos;
00184 my_off_t end_of_file_local= ~(my_off_t) 0;
00185
00186 file= file_arg;
00187 type= TYPE_NOT_SET;
00188 pos_in_file= seek_offset;
00189 pre_close = pre_read = post_read = 0;
00190 arg = 0;
00191 alloced_buffer = 0;
00192 buffer=0;
00193 seek_not_done= 0;
00194
00195 if (file >= 0)
00196 {
00197 pos= lseek(file, 0, SEEK_CUR);
00198 if ((pos == MY_FILEPOS_ERROR) && (errno == ESPIPE))
00199 {
00200
00201
00202
00203
00204 seek_not_done= 0;
00205
00206
00207
00208
00209
00210 assert(seek_offset == 0);
00211 }
00212 else
00213 seek_not_done= test(seek_offset != (my_off_t)pos);
00214 }
00215
00216 if (!cachesize && !(cachesize= my_default_record_cache_size))
00217 return 1;
00218 min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
00219 if (type_arg == READ_CACHE)
00220 {
00221 if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))
00222 {
00223
00224 end_of_file_local=lseek(file,0L,SEEK_END);
00225
00226 seek_not_done= end_of_file_local == seek_offset ? 0 : 1;
00227 if (end_of_file_local < seek_offset)
00228 end_of_file_local=seek_offset;
00229
00230 if ((my_off_t) cachesize > end_of_file_local-seek_offset+IO_SIZE*2-1)
00231 {
00232 cachesize= (size_t) (end_of_file_local-seek_offset)+IO_SIZE*2-1;
00233 use_async_io=0;
00234 }
00235 }
00236 }
00237 cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
00238 if (type_arg != READ_NET && type_arg != WRITE_NET)
00239 {
00240
00241 cachesize= ((cachesize + min_cache-1) & ~(min_cache-1));
00242 for (;;)
00243 {
00244 size_t buffer_block;
00245 if (cachesize < min_cache)
00246 cachesize = min_cache;
00247 buffer_block= cachesize;
00248 if ((type_arg == READ_CACHE) and (not global_read_buffer.add(buffer_block)))
00249 {
00250 my_error(ER_OUT_OF_GLOBAL_READMEMORY, MYF(ME_ERROR+ME_WAITTANG));
00251 return 2;
00252 }
00253
00254 if ((buffer=
00255 (unsigned char*) malloc(buffer_block)) != 0)
00256 {
00257 write_buffer=buffer;
00258 alloced_buffer= true;
00259 break;
00260 }
00261 if (cachesize == min_cache)
00262 {
00263 if (type_arg == READ_CACHE)
00264 global_read_buffer.sub(buffer_block);
00265 return 2;
00266 }
00267
00268 if (type_arg == READ_CACHE)
00269 global_read_buffer.sub(buffer_block);
00270 cachesize= (cachesize*3/4 & ~(min_cache-1));
00271 }
00272 }
00273
00274 read_length=buffer_length=cachesize;
00275 myflags=cache_myflags & ~(MY_NABP | MY_FNABP);
00276 request_pos= read_pos= write_pos = buffer;
00277
00278 if (type_arg == WRITE_CACHE)
00279 write_end=
00280 buffer+buffer_length- (seek_offset & (IO_SIZE-1));
00281 else
00282 read_end=buffer;
00283
00284
00285 end_of_file= end_of_file_local;
00286 error= 0;
00287 type= type_arg;
00288 init_functions();
00289 #ifdef HAVE_AIOWAIT
00290 if (use_async_io && ! my_disable_async_io)
00291 {
00292 read_length/=2;
00293 read_function=_my_b_async_read;
00294 }
00295 inited= aio_result.pending= 0;
00296 #endif
00297 return 0;
00298 }
00299
00300
00301
00302 #ifdef HAVE_AIOWAIT
00303 static void my_aiowait(my_aio_result *result)
00304 {
00305 if (result->pending)
00306 {
00307 struct aio_result_t *tmp;
00308 for (;;)
00309 {
00310 if ((int) (tmp=aiowait((struct timeval *) 0)) == -1)
00311 {
00312 if (errno == EINTR)
00313 continue;
00314 result->pending=0;
00315 break;
00316 }
00317 ((my_aio_result*) tmp)->pending=0;
00318 if ((my_aio_result*) tmp == result)
00319 break;
00320 }
00321 }
00322 }
00323 #endif
00324
00335 bool st_io_cache::reinit_io_cache(enum cache_type type_arg,
00336 my_off_t seek_offset,
00337 bool use_async_io,
00338 bool clear_cache)
00339 {
00340
00341 assert(type_arg != READ_NET && type != READ_NET &&
00342 type_arg != WRITE_NET && type != WRITE_NET);
00343
00344
00345 if (! clear_cache &&
00346 seek_offset >= pos_in_file &&
00347 seek_offset <= my_b_tell(this))
00348 {
00349
00350 unsigned char *pos;
00351 if (type == WRITE_CACHE && type_arg == READ_CACHE)
00352 {
00353 read_end=write_pos;
00354 end_of_file=my_b_tell(this);
00355
00356
00357
00358
00359 seek_not_done= (file != -1);
00360 }
00361 else if (type_arg == WRITE_CACHE)
00362 {
00363 if (type == READ_CACHE)
00364 {
00365 write_end=write_buffer+buffer_length;
00366 seek_not_done=1;
00367 }
00368 end_of_file = ~(my_off_t) 0;
00369 }
00370 pos=request_pos+(seek_offset-pos_in_file);
00371 if (type_arg == WRITE_CACHE)
00372 write_pos=pos;
00373 else
00374 read_pos= pos;
00375 #ifdef HAVE_AIOWAIT
00376 my_aiowait(&aio_result);
00377 #endif
00378 }
00379 else
00380 {
00381
00382
00383
00384
00385 if (type == WRITE_CACHE && type_arg == READ_CACHE)
00386 end_of_file=my_b_tell(this);
00387
00388 if (!clear_cache && my_b_flush_io_cache(this, 1))
00389 return 1;
00390 pos_in_file=seek_offset;
00391
00392 seek_not_done=1;
00393 request_pos=read_pos=write_pos=buffer;
00394 if (type_arg == READ_CACHE)
00395 {
00396 read_end=buffer;
00397 }
00398 else
00399 {
00400 write_end=(buffer + buffer_length -
00401 (seek_offset & (IO_SIZE-1)));
00402 end_of_file= ~(my_off_t) 0;
00403 }
00404 }
00405 type= type_arg;
00406 error=0;
00407 init_functions();
00408
00409 #ifdef HAVE_AIOWAIT
00410 if (use_async_io && ! my_disable_async_io &&
00411 ((uint32_t) buffer_length <
00412 (uint32_t) (end_of_file - seek_offset)))
00413 {
00414 read_length=buffer_length/2;
00415 read_function=_my_b_async_read;
00416 }
00417 inited= 0;
00418 #else
00419 (void)use_async_io;
00420 #endif
00421 return 0;
00422 }
00423
00444 static int _my_b_read(st_io_cache *info, unsigned char *Buffer, size_t Count)
00445 {
00446 size_t length_local,diff_length,left_length, max_length;
00447 my_off_t pos_in_file_local;
00448
00449 if ((left_length= (size_t) (info->read_end-info->read_pos)))
00450 {
00451 assert(Count >= left_length);
00452 memcpy(Buffer,info->read_pos, left_length);
00453 Buffer+=left_length;
00454 Count-=left_length;
00455 }
00456
00457
00458 pos_in_file_local=info->pos_in_file+ (size_t) (info->read_end - info->buffer);
00459
00460
00461
00462
00463
00464
00465
00466 if (info->seek_not_done)
00467 {
00468 if ((lseek(info->file,pos_in_file_local,SEEK_SET) != MY_FILEPOS_ERROR))
00469 {
00470
00471 info->seek_not_done= 0;
00472 }
00473 else
00474 {
00475
00476
00477
00478
00479
00480 assert(errno != ESPIPE);
00481 info->error= -1;
00482 return(1);
00483 }
00484 }
00485
00486 diff_length= (size_t) (pos_in_file_local & (IO_SIZE-1));
00487 if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
00488 {
00489 size_t read_length;
00490 if (info->end_of_file <= pos_in_file_local)
00491 {
00492 info->error= (int) left_length;
00493 return(1);
00494 }
00495 length_local=(Count & (size_t) ~(IO_SIZE-1))-diff_length;
00496 if ((read_length= my_read(info->file,Buffer, length_local, info->myflags)) != length_local)
00497 {
00498 info->error= (read_length == (size_t) -1 ? -1 :
00499 (int) (read_length+left_length));
00500 return(1);
00501 }
00502 Count-= length_local;
00503 Buffer+= length_local;
00504 pos_in_file_local+= length_local;
00505 left_length+= length_local;
00506 diff_length=0;
00507 }
00508
00509 max_length= info->read_length-diff_length;
00510 if (info->type != READ_FIFO &&
00511 max_length > (info->end_of_file - pos_in_file_local))
00512 max_length= (size_t) (info->end_of_file - pos_in_file_local);
00513 if (!max_length)
00514 {
00515 if (Count)
00516 {
00517 info->error= static_cast<int>(left_length);
00518 return(1);
00519 }
00520 length_local=0;
00521 }
00522 else if (( length_local= my_read(info->file,info->buffer, max_length,
00523 info->myflags)) < Count ||
00524 length_local == (size_t) -1)
00525 {
00526 if ( length_local != (size_t) -1)
00527 memcpy(Buffer, info->buffer, length_local);
00528 info->pos_in_file= pos_in_file_local;
00529 info->error= length_local == (size_t) -1 ? -1 : (int) ( length_local+left_length);
00530 info->read_pos=info->read_end=info->buffer;
00531 return(1);
00532 }
00533 info->read_pos=info->buffer+Count;
00534 info->read_end=info->buffer+ length_local;
00535 info->pos_in_file=pos_in_file_local;
00536 memcpy(Buffer, info->buffer, Count);
00537 return(0);
00538 }
00539
00540
00541 #ifdef HAVE_AIOWAIT
00542
00555 int _my_b_async_read(st_io_cache *info, unsigned char *Buffer, size_t Count)
00556 {
00557 size_t length_local,read_length,diff_length,left_length,use_length,org_Count;
00558 size_t max_length;
00559 my_off_t next_pos_in_file;
00560 unsigned char *read_buffer;
00561
00562 memcpy(Buffer,info->read_pos,
00563 (left_length= (size_t) (info->read_end-info->read_pos)));
00564 Buffer+=left_length;
00565 org_Count=Count;
00566 Count-=left_length;
00567
00568 if (info->inited)
00569 {
00570 info->inited=0;
00571 my_aiowait(&info->aio_result);
00572 if (info->aio_result.result.aio_errno)
00573 {
00574 if (info->myflags & MY_WME)
00575 my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG),
00576 my_filename(info->file),
00577 info->aio_result.result.aio_errno);
00578 errno=info->aio_result.result.aio_errno;
00579 info->error= -1;
00580 return(1);
00581 }
00582 if (! (read_length= (size_t) info->aio_result.result.aio_return) ||
00583 read_length == (size_t) -1)
00584 {
00585 errno=0;
00586 info->error= (read_length == (size_t) -1 ? -1 :
00587 (int) (read_length+left_length));
00588 return(1);
00589 }
00590 info->pos_in_file+= (size_t) (info->read_end - info->request_pos);
00591
00592 if (info->request_pos != info->buffer)
00593 info->request_pos=info->buffer;
00594 else
00595 info->request_pos=info->buffer+info->read_length;
00596 info->read_pos=info->request_pos;
00597 next_pos_in_file=info->aio_read_pos+read_length;
00598
00599
00600
00601
00602 if (info->aio_read_pos < info->pos_in_file)
00603 {
00604 if (info->aio_read_pos + read_length < info->pos_in_file)
00605 {
00606 read_length=0;
00607 next_pos_in_file=info->pos_in_file;
00608 }
00609 else
00610 {
00611 my_off_t offset= (info->pos_in_file - info->aio_read_pos);
00612 info->pos_in_file=info->aio_read_pos;
00613 info->read_pos=info->request_pos+offset;
00614 read_length-=offset;
00615 }
00616 }
00617
00618 length_local=min(Count,read_length);
00619 memcpy(Buffer,info->read_pos,(size_t) length_local);
00620 Buffer+=length_local;
00621 Count-=length_local;
00622 left_length+=length_local;
00623 info->read_end=info->rc_pos+read_length;
00624 info->read_pos+=length_local;
00625 }
00626 else
00627 next_pos_in_file=(info->pos_in_file+ (size_t)
00628 (info->read_end - info->request_pos));
00629
00630
00631 if (Count)
00632 {
00633 if (next_pos_in_file == info->end_of_file)
00634 {
00635 info->error=(int) (read_length+left_length);
00636 return 1;
00637 }
00638
00639 if (lseek(info->file,next_pos_in_file,SEEK_SET) == MY_FILEPOS_ERROR)
00640 {
00641 info->error= -1;
00642 return (1);
00643 }
00644
00645 read_length=IO_SIZE*2- (size_t) (next_pos_in_file & (IO_SIZE-1));
00646 if (Count < read_length)
00647 {
00648 if ((read_length=my_read(info->file,info->request_pos,
00649 read_length, info->myflags)) == (size_t) -1)
00650 return info->error= -1;
00651 use_length=min(Count,read_length);
00652 memcpy(Buffer,info->request_pos,(size_t) use_length);
00653 info->read_pos=info->request_pos+Count;
00654 info->read_end=info->request_pos+read_length;
00655 info->pos_in_file=next_pos_in_file;
00656 next_pos_in_file+=read_length;
00657
00658 if (Count != use_length)
00659 {
00660 if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count)
00661 my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG),
00662 my_filename(info->file),errno);
00663 info->error=(int) (read_length+left_length);
00664 return 1;
00665 }
00666 }
00667 else
00668 {
00669 if ((read_length= my_read(info->file,Buffer, Count,info->myflags))
00670 != Count)
00671 {
00672 info->error= read_length == (size_t) -1 ? -1 : read_length+left_length;
00673 return 1;
00674 }
00675 info->read_pos=info->read_end=info->request_pos;
00676 info->pos_in_file=(next_pos_in_file+=Count);
00677 }
00678 }
00679
00680
00681 diff_length=(next_pos_in_file & (IO_SIZE-1));
00682 max_length= info->read_length - diff_length;
00683 if (max_length > info->end_of_file - next_pos_in_file)
00684 max_length= (size_t) (info->end_of_file - next_pos_in_file);
00685
00686 if (info->request_pos != info->buffer)
00687 read_buffer=info->buffer;
00688 else
00689 read_buffer=info->buffer+info->read_length;
00690 info->aio_read_pos=next_pos_in_file;
00691 if (max_length)
00692 {
00693 info->aio_result.result.aio_errno=AIO_INPROGRESS;
00694 if (aioread(info->file,read_buffer, max_length,
00695 (my_off_t) next_pos_in_file,SEEK_SET,
00696 &info->aio_result.result))
00697 {
00698 errno=errno;
00699 if (info->request_pos != info->buffer)
00700 {
00701 memmove(info->buffer, info->request_pos,
00702 (size_t) (info->read_end - info->read_pos));
00703 info->request_pos=info->buffer;
00704 info->read_pos-=info->read_length;
00705 info->read_end-=info->read_length;
00706 }
00707 info->read_length=info->buffer_length;
00708 info->read_function=_my_b_read;
00709 }
00710 else
00711 info->inited=info->aio_result.pending=1;
00712 }
00713 return 0;
00714 }
00715 #endif
00716
00717
00722 int _my_b_get(st_io_cache *info)
00723 {
00724 unsigned char buff;
00725 IO_CACHE_CALLBACK pre_read,post_read;
00726 if ((pre_read = info->pre_read))
00727 (*pre_read)(info);
00728 if ((*(info)->read_function)(info,&buff,1))
00729 return my_b_EOF;
00730 if ((post_read = info->post_read))
00731 (*post_read)(info);
00732 return (int) (unsigned char) buff;
00733 }
00734
00743 int _my_b_write(st_io_cache *info, const unsigned char *Buffer, size_t Count)
00744 {
00745 size_t rest_length,length_local;
00746
00747 if (info->pos_in_file+info->buffer_length > info->end_of_file)
00748 {
00749 errno=EFBIG;
00750 return info->error = -1;
00751 }
00752
00753 rest_length= (size_t) (info->write_end - info->write_pos);
00754 memcpy(info->write_pos,Buffer,(size_t) rest_length);
00755 Buffer+=rest_length;
00756 Count-=rest_length;
00757 info->write_pos+=rest_length;
00758
00759 if (my_b_flush_io_cache(info,1))
00760 return 1;
00761 if (Count >= IO_SIZE)
00762 {
00763 length_local=Count & (size_t) ~(IO_SIZE-1);
00764 if (info->seek_not_done)
00765 {
00766
00767
00768
00769
00770
00771
00772 if (lseek(info->file,info->pos_in_file,SEEK_SET))
00773 {
00774 info->error= -1;
00775 return (1);
00776 }
00777 info->seek_not_done=0;
00778 }
00779 if (my_write(info->file, Buffer, length_local, info->myflags | MY_NABP))
00780 return info->error= -1;
00781
00782 Count-=length_local;
00783 Buffer+=length_local;
00784 info->pos_in_file+=length_local;
00785 }
00786 memcpy(info->write_pos,Buffer,(size_t) Count);
00787 info->write_pos+=Count;
00788 return 0;
00789 }
00790
00797 int my_block_write(st_io_cache *info, const unsigned char *Buffer, size_t Count,
00798 my_off_t pos)
00799 {
00800 size_t length_local;
00801 int error=0;
00802
00803 if (pos < info->pos_in_file)
00804 {
00805
00806 if (pos + Count <= info->pos_in_file)
00807 return (pwrite(info->file, Buffer, Count, pos) == 0);
00808
00809 length_local= (uint32_t) (info->pos_in_file - pos);
00810 if (pwrite(info->file, Buffer, length_local, pos) == 0)
00811 info->error= error= -1;
00812 Buffer+=length_local;
00813 pos+= length_local;
00814 Count-= length_local;
00815 }
00816
00817
00818 length_local= (size_t) (info->write_end - info->buffer);
00819 if (pos < info->pos_in_file + length_local)
00820 {
00821 size_t offset= (size_t) (pos - info->pos_in_file);
00822 length_local-=offset;
00823 if (length_local > Count)
00824 length_local=Count;
00825 memcpy(info->buffer+offset, Buffer, length_local);
00826 Buffer+=length_local;
00827 Count-= length_local;
00828
00829 if (info->buffer+length_local > info->write_pos)
00830 info->write_pos=info->buffer+length_local;
00831 if (!Count)
00832 return (error);
00833 }
00834
00835 if (_my_b_write(info, Buffer, Count))
00836 error= -1;
00837 return error;
00838 }
00839
00844 int my_b_flush_io_cache(st_io_cache *info, int need_append_buffer_lock)
00845 {
00846 size_t length_local;
00847 bool append_cache= false;
00848 my_off_t pos_in_file_local;
00849
00850 if (info->type == WRITE_CACHE || append_cache)
00851 {
00852 if (info->file == -1)
00853 {
00854 if (info->real_open_cached_file())
00855 return((info->error= -1));
00856 }
00857 lock_append_buffer(info, need_append_buffer_lock);
00858
00859 if ((length_local=(size_t) (info->write_pos - info->write_buffer)))
00860 {
00861 pos_in_file_local=info->pos_in_file;
00862
00863
00864
00865
00866 if (!append_cache && info->seek_not_done)
00867 {
00868 if (lseek(info->file,pos_in_file_local,SEEK_SET) == MY_FILEPOS_ERROR)
00869 {
00870 unlock_append_buffer(info, need_append_buffer_lock);
00871 return((info->error= -1));
00872 }
00873 if (!append_cache)
00874 info->seek_not_done=0;
00875 }
00876 if (!append_cache)
00877 info->pos_in_file+=length_local;
00878 info->write_end= (info->write_buffer+info->buffer_length-
00879 ((pos_in_file_local+length_local) & (IO_SIZE-1)));
00880
00881 if (my_write(info->file,info->write_buffer,length_local,
00882 info->myflags | MY_NABP))
00883 info->error= -1;
00884 else
00885 info->error= 0;
00886 if (!append_cache)
00887 {
00888 set_if_bigger(info->end_of_file,(pos_in_file_local+length_local));
00889 }
00890 else
00891 {
00892 info->end_of_file+=(info->write_pos-info->append_read_pos);
00893 my_off_t tell_ret= lseek(info->file, 0, SEEK_CUR);
00894 assert(info->end_of_file == tell_ret);
00895 }
00896
00897 info->append_read_pos=info->write_pos=info->write_buffer;
00898 unlock_append_buffer(info, need_append_buffer_lock);
00899 return(info->error);
00900 }
00901 }
00902 #ifdef HAVE_AIOWAIT
00903 else if (info->type != READ_NET)
00904 {
00905 my_aiowait(&info->aio_result);
00906 info->inited=0;
00907 }
00908 #endif
00909 unlock_append_buffer(info, need_append_buffer_lock);
00910 return(0);
00911 }
00912
00927 int st_io_cache::end_io_cache()
00928 {
00929 int _error=0;
00930
00931 if (pre_close)
00932 {
00933 (*pre_close)(this);
00934 pre_close= 0;
00935 }
00936 if (alloced_buffer)
00937 {
00938 if (type == READ_CACHE)
00939 global_read_buffer.sub(buffer_length);
00940 alloced_buffer=0;
00941 if (file != -1)
00942 _error= my_b_flush_io_cache(this, 1);
00943 free((unsigned char*) buffer);
00944 buffer=read_pos=(unsigned char*) 0;
00945 }
00946
00947 return _error;
00948 }
00949
00950 }
00951 }