OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 // Read the file in blocks of size 64k. | 7 // Read the file in blocks of size 64k. |
8 const int _BLOCK_SIZE = 64 * 1024; | 8 const int _BLOCK_SIZE = 64 * 1024; |
9 | 9 |
10 | 10 |
(...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
524 if (result is OSError) { | 524 if (result is OSError) { |
525 throw new FileException(msg, path, result); | 525 throw new FileException(msg, path, result); |
526 } | 526 } |
527 } | 527 } |
528 } | 528 } |
529 | 529 |
530 | 530 |
531 class _RandomAccessFile implements RandomAccessFile { | 531 class _RandomAccessFile implements RandomAccessFile { |
532 final String path; | 532 final String path; |
533 int _id; | 533 int _id; |
| 534 bool _asyncDispatched = false; |
534 SendPort _fileService; | 535 SendPort _fileService; |
535 | 536 |
536 _RandomAccessFile(int this._id, String this.path); | 537 _RandomAccessFile(int this._id, String this.path); |
537 | 538 |
538 Future<RandomAccessFile> close() { | 539 Future<RandomAccessFile> close() { |
539 if (closed) return _closedException(); | 540 return _dispatch(_FILE_CLOSE, [_id], markClosed: true).then((result) { |
540 // Set the id_ to 0 (NULL) to ensure the no more async requests | |
541 // can be issued for this file. | |
542 int id = _id; | |
543 _id = 0; | |
544 return _IOService.dispatch(_FILE_CLOSE, [id]).then((result) { | |
545 if (result != -1) { | 541 if (result != -1) { |
546 _id = result; | 542 _id = result; |
547 return this; | 543 return this; |
548 } else { | 544 } else { |
549 throw new FileException("Cannot close file", path); | 545 throw new FileException("Cannot close file", path); |
550 } | 546 } |
551 }); | 547 }); |
552 } | 548 } |
553 | 549 |
554 external static int _close(int id); | 550 external static int _close(int id); |
555 | 551 |
556 void closeSync() { | 552 void closeSync() { |
557 _checkNotClosed(); | 553 _checkAvailable(); |
558 var id = _close(_id); | 554 var id = _close(_id); |
559 if (id == -1) { | 555 if (id == -1) { |
560 throw new FileException("Cannot close file", path); | 556 throw new FileException("Cannot close file", path); |
561 } | 557 } |
562 _id = id; | 558 _id = id; |
563 } | 559 } |
564 | 560 |
565 Future<int> readByte() { | 561 Future<int> readByte() { |
566 if (closed) return _closedException(); | 562 return _dispatch(_FILE_READ_BYTE, [_id]).then((response) { |
567 return _IOService.dispatch(_FILE_READ_BYTE, [_id]).then((response) { | |
568 if (_isErrorResponse(response)) { | 563 if (_isErrorResponse(response)) { |
569 throw _exceptionFromResponse(response, "readByte failed", path); | 564 throw _exceptionFromResponse(response, "readByte failed", path); |
570 } | 565 } |
571 return response; | 566 return response; |
572 }); | 567 }); |
573 } | 568 } |
574 | 569 |
575 external static _readByte(int id); | 570 external static _readByte(int id); |
576 | 571 |
577 int readByteSync() { | 572 int readByteSync() { |
578 _checkNotClosed(); | 573 _checkAvailable(); |
579 var result = _readByte(_id); | 574 var result = _readByte(_id); |
580 if (result is OSError) { | 575 if (result is OSError) { |
581 throw new FileException("readByte failed", path, result); | 576 throw new FileException("readByte failed", path, result); |
582 } | 577 } |
583 return result; | 578 return result; |
584 } | 579 } |
585 | 580 |
586 Future<List<int>> read(int bytes) { | 581 Future<List<int>> read(int bytes) { |
587 if (bytes is !int) { | 582 if (bytes is !int) { |
588 throw new ArgumentError(bytes); | 583 throw new ArgumentError(bytes); |
589 } | 584 } |
590 if (closed) return _closedException(); | 585 return _dispatch(_FILE_READ, [_id, bytes]).then((response) { |
591 return _IOService.dispatch(_FILE_READ, [_id, bytes]).then((response) { | |
592 if (_isErrorResponse(response)) { | 586 if (_isErrorResponse(response)) { |
593 throw _exceptionFromResponse(response, "read failed", path); | 587 throw _exceptionFromResponse(response, "read failed", path); |
594 } | 588 } |
595 return response[1]; | 589 return response[1]; |
596 }); | 590 }); |
597 } | 591 } |
598 | 592 |
599 external static _read(int id, int bytes); | 593 external static _read(int id, int bytes); |
600 | 594 |
601 List<int> readSync(int bytes) { | 595 List<int> readSync(int bytes) { |
602 _checkNotClosed(); | 596 _checkAvailable(); |
603 if (bytes is !int) { | 597 if (bytes is !int) { |
604 throw new ArgumentError(bytes); | 598 throw new ArgumentError(bytes); |
605 } | 599 } |
606 var result = _read(_id, bytes); | 600 var result = _read(_id, bytes); |
607 if (result is OSError) { | 601 if (result is OSError) { |
608 throw new FileException("readSync failed", path, result); | 602 throw new FileException("readSync failed", path, result); |
609 } | 603 } |
610 return result; | 604 return result; |
611 } | 605 } |
612 | 606 |
613 Future<int> readInto(List<int> buffer, [int start, int end]) { | 607 Future<int> readInto(List<int> buffer, [int start, int end]) { |
614 if (buffer is !List || | 608 if (buffer is !List || |
615 (start != null && start is !int) || | 609 (start != null && start is !int) || |
616 (end != null && end is !int)) { | 610 (end != null && end is !int)) { |
617 throw new ArgumentError(); | 611 throw new ArgumentError(); |
618 } | 612 } |
619 if (closed) return _closedException(); | |
620 if (start == null) start = 0; | 613 if (start == null) start = 0; |
621 if (end == null) end = buffer.length; | 614 if (end == null) end = buffer.length; |
622 int length = end - start; | 615 int length = end - start; |
623 return _IOService.dispatch(_FILE_READ_INTO, [_id, length]).then((response) { | 616 return _dispatch(_FILE_READ_INTO, [_id, length]).then((response) { |
624 if (_isErrorResponse(response)) { | 617 if (_isErrorResponse(response)) { |
625 throw _exceptionFromResponse(response, "readInto failed", path); | 618 throw _exceptionFromResponse(response, "readInto failed", path); |
626 } | 619 } |
627 var read = response[1]; | 620 var read = response[1]; |
628 var data = response[2]; | 621 var data = response[2]; |
629 buffer.setRange(start, start + read, data); | 622 buffer.setRange(start, start + read, data); |
630 return read; | 623 return read; |
631 }); | 624 }); |
632 } | 625 } |
633 | 626 |
634 static void _checkReadWriteListArguments(int length, int start, int end) { | 627 static void _checkReadWriteListArguments(int length, int start, int end) { |
635 if (start < 0) throw new RangeError.value(start); | 628 if (start < 0) throw new RangeError.value(start); |
636 if (end < start) throw new RangeError.value(end); | 629 if (end < start) throw new RangeError.value(end); |
637 if (end > length) { | 630 if (end > length) { |
638 throw new RangeError.value(end); | 631 throw new RangeError.value(end); |
639 } | 632 } |
640 } | 633 } |
641 | 634 |
642 external static _readInto(int id, List<int> buffer, int start, int end); | 635 external static _readInto(int id, List<int> buffer, int start, int end); |
643 | 636 |
644 int readIntoSync(List<int> buffer, [int start, int end]) { | 637 int readIntoSync(List<int> buffer, [int start, int end]) { |
645 _checkNotClosed(); | 638 _checkAvailable(); |
646 if (buffer is !List || | 639 if (buffer is !List || |
647 (start != null && start is !int) || | 640 (start != null && start is !int) || |
648 (end != null && end is !int)) { | 641 (end != null && end is !int)) { |
649 throw new ArgumentError(); | 642 throw new ArgumentError(); |
650 } | 643 } |
651 if (start == null) start = 0; | 644 if (start == null) start = 0; |
652 if (end == null) end = buffer.length; | 645 if (end == null) end = buffer.length; |
653 if (end == start) return 0; | 646 if (end == start) return 0; |
654 _checkReadWriteListArguments(buffer.length, start, end); | 647 _checkReadWriteListArguments(buffer.length, start, end); |
655 var result = _readInto(_id, buffer, start, end); | 648 var result = _readInto(_id, buffer, start, end); |
656 if (result is OSError) { | 649 if (result is OSError) { |
657 throw new FileException("readInto failed", path, result); | 650 throw new FileException("readInto failed", path, result); |
658 } | 651 } |
659 return result; | 652 return result; |
660 } | 653 } |
661 | 654 |
662 Future<RandomAccessFile> writeByte(int value) { | 655 Future<RandomAccessFile> writeByte(int value) { |
663 if (value is !int) { | 656 if (value is !int) { |
664 throw new ArgumentError(value); | 657 throw new ArgumentError(value); |
665 } | 658 } |
666 if (closed) return _closedException(); | 659 return _dispatch(_FILE_WRITE_BYTE, [_id, value]).then((response) { |
667 return _IOService.dispatch(_FILE_WRITE_BYTE, [_id, value]).then((response) { | |
668 if (_isErrorResponse(response)) { | 660 if (_isErrorResponse(response)) { |
669 throw _exceptionFromResponse(response, "writeByte failed", path); | 661 throw _exceptionFromResponse(response, "writeByte failed", path); |
670 } | 662 } |
671 return this; | 663 return this; |
672 }); | 664 }); |
673 } | 665 } |
674 | 666 |
675 external static _writeByte(int id, int value); | 667 external static _writeByte(int id, int value); |
676 | 668 |
677 int writeByteSync(int value) { | 669 int writeByteSync(int value) { |
678 _checkNotClosed(); | 670 _checkAvailable(); |
679 if (value is !int) { | 671 if (value is !int) { |
680 throw new ArgumentError(value); | 672 throw new ArgumentError(value); |
681 } | 673 } |
682 var result = _writeByte(_id, value); | 674 var result = _writeByte(_id, value); |
683 if (result is OSError) { | 675 if (result is OSError) { |
684 throw new FileException("writeByte failed", path, result); | 676 throw new FileException("writeByte failed", path, result); |
685 } | 677 } |
686 return result; | 678 return result; |
687 } | 679 } |
688 | 680 |
689 Future<RandomAccessFile> writeFrom(List<int> buffer, [int start, int end]) { | 681 Future<RandomAccessFile> writeFrom(List<int> buffer, [int start, int end]) { |
690 if ((buffer is !List && buffer is !ByteData) || | 682 if ((buffer is !List && buffer is !ByteData) || |
691 (start != null && start is !int) || | 683 (start != null && start is !int) || |
692 (end != null && end is !int)) { | 684 (end != null && end is !int)) { |
693 throw new ArgumentError("Invalid arguments to writeFrom"); | 685 throw new ArgumentError("Invalid arguments to writeFrom"); |
694 } | 686 } |
695 | 687 |
696 if (closed) return _closedException(); | |
697 | |
698 _BufferAndStart result; | 688 _BufferAndStart result; |
699 try { | 689 try { |
700 result = _ensureFastAndSerializableByteData(buffer, start, end); | 690 result = _ensureFastAndSerializableByteData(buffer, start, end); |
701 } catch (e) { | 691 } catch (e) { |
702 return new Future.error(e); | 692 return new Future.error(e); |
703 } | 693 } |
704 | 694 |
705 List request = new List(4); | 695 List request = new List(4); |
706 request[0] = _id; | 696 request[0] = _id; |
707 request[1] = result.buffer; | 697 request[1] = result.buffer; |
708 request[2] = result.start; | 698 request[2] = result.start; |
709 request[3] = end - (start - result.start); | 699 request[3] = end - (start - result.start); |
710 return _IOService.dispatch(_FILE_WRITE_FROM, request).then((response) { | 700 return _dispatch(_FILE_WRITE_FROM, request).then((response) { |
711 if (_isErrorResponse(response)) { | 701 if (_isErrorResponse(response)) { |
712 throw _exceptionFromResponse(response, "writeFrom failed", path); | 702 throw _exceptionFromResponse(response, "writeFrom failed", path); |
713 } | 703 } |
714 return this; | 704 return this; |
715 }); | 705 }); |
716 } | 706 } |
717 | 707 |
718 external static _writeFrom(int id, List<int> buffer, int start, int end); | 708 external static _writeFrom(int id, List<int> buffer, int start, int end); |
719 | 709 |
720 void writeFromSync(List<int> buffer, [int start, int end]) { | 710 void writeFromSync(List<int> buffer, [int start, int end]) { |
721 _checkNotClosed(); | 711 _checkAvailable(); |
722 if (buffer is !List || | 712 if (buffer is !List || |
723 (start != null && start is !int) || | 713 (start != null && start is !int) || |
724 (end != null && end is !int)) { | 714 (end != null && end is !int)) { |
725 throw new ArgumentError("Invalid arguments to writeFromSync"); | 715 throw new ArgumentError("Invalid arguments to writeFromSync"); |
726 } | 716 } |
727 if (start == null) start = 0; | 717 if (start == null) start = 0; |
728 if (end == null) end = buffer.length; | 718 if (end == null) end = buffer.length; |
729 if (end == start) return; | 719 if (end == start) return; |
730 _checkReadWriteListArguments(buffer.length, start, end); | 720 _checkReadWriteListArguments(buffer.length, start, end); |
731 _BufferAndStart bufferAndStart = | 721 _BufferAndStart bufferAndStart = |
(...skipping 18 matching lines...) Expand all Loading... |
750 | 740 |
751 void writeStringSync(String string, {Encoding encoding: UTF8}) { | 741 void writeStringSync(String string, {Encoding encoding: UTF8}) { |
752 if (encoding is! Encoding) { | 742 if (encoding is! Encoding) { |
753 throw new ArgumentError(encoding); | 743 throw new ArgumentError(encoding); |
754 } | 744 } |
755 var data = encoding.encode(string); | 745 var data = encoding.encode(string); |
756 writeFromSync(data, 0, data.length); | 746 writeFromSync(data, 0, data.length); |
757 } | 747 } |
758 | 748 |
759 Future<int> position() { | 749 Future<int> position() { |
760 if (closed) return _closedException(); | 750 return _dispatch(_FILE_POSITION, [_id]).then((response) { |
761 return _IOService.dispatch(_FILE_POSITION, [_id]).then((response) { | |
762 if (_isErrorResponse(response)) { | 751 if (_isErrorResponse(response)) { |
763 throw _exceptionFromResponse(response, "position failed", path); | 752 throw _exceptionFromResponse(response, "position failed", path); |
764 } | 753 } |
765 return response; | 754 return response; |
766 }); | 755 }); |
767 } | 756 } |
768 | 757 |
769 external static _position(int id); | 758 external static _position(int id); |
770 | 759 |
771 int positionSync() { | 760 int positionSync() { |
772 _checkNotClosed(); | 761 _checkAvailable(); |
773 var result = _position(_id); | 762 var result = _position(_id); |
774 if (result is OSError) { | 763 if (result is OSError) { |
775 throw new FileException("position failed", path, result); | 764 throw new FileException("position failed", path, result); |
776 } | 765 } |
777 return result; | 766 return result; |
778 } | 767 } |
779 | 768 |
780 Future<RandomAccessFile> setPosition(int position) { | 769 Future<RandomAccessFile> setPosition(int position) { |
781 if (closed) return _closedException(); | 770 return _dispatch(_FILE_SET_POSITION, [_id, position]) |
782 return _IOService.dispatch(_FILE_SET_POSITION, [_id, position]) | |
783 .then((response) { | 771 .then((response) { |
784 if (_isErrorResponse(response)) { | 772 if (_isErrorResponse(response)) { |
785 throw _exceptionFromResponse(response, "setPosition failed", path); | 773 throw _exceptionFromResponse(response, "setPosition failed", path); |
786 } | 774 } |
787 return this; | 775 return this; |
788 }); | 776 }); |
789 } | 777 } |
790 | 778 |
791 external static _setPosition(int id, int position); | 779 external static _setPosition(int id, int position); |
792 | 780 |
793 void setPositionSync(int position) { | 781 void setPositionSync(int position) { |
794 _checkNotClosed(); | 782 _checkAvailable(); |
795 var result = _setPosition(_id, position); | 783 var result = _setPosition(_id, position); |
796 if (result is OSError) { | 784 if (result is OSError) { |
797 throw new FileException("setPosition failed", path, result); | 785 throw new FileException("setPosition failed", path, result); |
798 } | 786 } |
799 } | 787 } |
800 | 788 |
801 Future<RandomAccessFile> truncate(int length) { | 789 Future<RandomAccessFile> truncate(int length) { |
802 if (closed) return _closedException(); | 790 return _dispatch(_FILE_TRUNCATE, [_id, length]).then((response) { |
803 return _IOService.dispatch(_FILE_TRUNCATE, [_id, length]).then((response) { | |
804 if (_isErrorResponse(response)) { | 791 if (_isErrorResponse(response)) { |
805 throw _exceptionFromResponse(response, "truncate failed", path); | 792 throw _exceptionFromResponse(response, "truncate failed", path); |
806 } | 793 } |
807 return this; | 794 return this; |
808 }); | 795 }); |
809 } | 796 } |
810 | 797 |
811 external static _truncate(int id, int length); | 798 external static _truncate(int id, int length); |
812 | 799 |
813 void truncateSync(int length) { | 800 void truncateSync(int length) { |
814 _checkNotClosed(); | 801 _checkAvailable(); |
815 var result = _truncate(_id, length); | 802 var result = _truncate(_id, length); |
816 if (result is OSError) { | 803 if (result is OSError) { |
817 throw new FileException("truncate failed", path, result); | 804 throw new FileException("truncate failed", path, result); |
818 } | 805 } |
819 } | 806 } |
820 | 807 |
821 Future<int> length() { | 808 Future<int> length() { |
822 if (closed) return _closedException(); | 809 return _dispatch(_FILE_LENGTH, [_id]).then((response) { |
823 return _IOService.dispatch(_FILE_LENGTH, [_id]).then((response) { | |
824 if (_isErrorResponse(response)) { | 810 if (_isErrorResponse(response)) { |
825 throw _exceptionFromResponse(response, "length failed", path); | 811 throw _exceptionFromResponse(response, "length failed", path); |
826 } | 812 } |
827 return response; | 813 return response; |
828 }); | 814 }); |
829 } | 815 } |
830 | 816 |
831 external static _length(int id); | 817 external static _length(int id); |
832 | 818 |
833 int lengthSync() { | 819 int lengthSync() { |
834 _checkNotClosed(); | 820 _checkAvailable(); |
835 var result = _length(_id); | 821 var result = _length(_id); |
836 if (result is OSError) { | 822 if (result is OSError) { |
837 throw new FileException("length failed", path, result); | 823 throw new FileException("length failed", path, result); |
838 } | 824 } |
839 return result; | 825 return result; |
840 } | 826 } |
841 | 827 |
842 Future<RandomAccessFile> flush() { | 828 Future<RandomAccessFile> flush() { |
843 if (closed) return _closedException(); | 829 return _dispatch(_FILE_FLUSH, [_id]).then((response) { |
844 return _IOService.dispatch(_FILE_FLUSH, [_id]).then((response) { | |
845 if (_isErrorResponse(response)) { | 830 if (_isErrorResponse(response)) { |
846 throw _exceptionFromResponse(response, | 831 throw _exceptionFromResponse(response, |
847 "flush failed", | 832 "flush failed", |
848 path); | 833 path); |
849 } | 834 } |
850 return this; | 835 return this; |
851 }); | 836 }); |
852 } | 837 } |
853 | 838 |
854 external static _flush(int id); | 839 external static _flush(int id); |
855 | 840 |
856 void flushSync() { | 841 void flushSync() { |
857 _checkNotClosed(); | 842 _checkAvailable(); |
858 var result = _flush(_id); | 843 var result = _flush(_id); |
859 if (result is OSError) { | 844 if (result is OSError) { |
860 throw new FileException("flush failed", path, result); | 845 throw new FileException("flush failed", path, result); |
861 } | 846 } |
862 } | 847 } |
863 | 848 |
864 bool get closed => _id == 0; | 849 bool get closed => _id == 0; |
865 | 850 |
866 void _checkNotClosed() { | 851 Future _dispatch(int request, List data, { bool markClosed: false }) { |
| 852 if (closed) { |
| 853 return new Future.error(new FileException("File closed", path)); |
| 854 } |
| 855 if (_asyncDispatched) { |
| 856 var msg = "An async operation is currently pending"; |
| 857 return new Future.error(new FileException(msg, path)); |
| 858 } |
| 859 if (markClosed) { |
| 860 // Set the id_ to 0 (NULL) to ensure the no more async requests |
| 861 // can be issued for this file. |
| 862 _id = 0; |
| 863 } |
| 864 _asyncDispatched = true; |
| 865 return _IOService.dispatch(request, data) |
| 866 .whenComplete(() { |
| 867 _asyncDispatched = false; |
| 868 }); |
| 869 } |
| 870 |
| 871 void _checkAvailable() { |
| 872 if (_asyncDispatched) { |
| 873 throw new FileException("An async operation is currently pending", path); |
| 874 } |
867 if (closed) { | 875 if (closed) { |
868 throw new FileException("File closed", path); | 876 throw new FileException("File closed", path); |
869 } | 877 } |
870 } | 878 } |
871 | |
872 Future _closedException() { | |
873 return new Future.error(new FileException("File closed", path)); | |
874 } | |
875 } | 879 } |
OLD | NEW |