@@ -667,26 +667,33 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags
667667 * call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
668668 *
669669 * Create an IO::Buffer for reading from +file+ by memory-mapping the file.
670- * +file_io + should be a +File+ instance, opened for reading.
670+ * +file + should be a +File+ instance, opened for reading or reading and writing .
671671 *
672672 * Optional +size+ and +offset+ of mapping can be specified.
673+ * Trying to map an empty file or specify +size+ of 0 will raise an error.
674+ * Valid values for +offset+ are system-dependent.
673675 *
674- * By default, the buffer would be immutable (read only); to create a writable
675- * mapping, you need to open a file in read-write mode, and explicitly pass
676- * +flags+ argument without IO::Buffer::IMMUTABLE.
676+ * By default, the buffer is writable and expects the file to be writable.
677+ * It is also shared, so several processes can use the same mapping.
678+ *
679+ * You can pass IO::Buffer::READONLY in +flags+ argument to make a read-only buffer;
680+ * this allows to work with files opened only for reading.
681+ * Specifying IO::Buffer::PRIVATE in +flags+ creates a private mapping,
682+ * which will not impact other processes or the underlying file.
683+ * It also allows updating a buffer created from a read-only file.
677684 *
678685 * File.write('test.txt', 'test')
679686 *
680687 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
681- * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
688+ * # => #<IO::Buffer 0x00000001014a0000+4 EXTERNAL MAPPED FILE SHARED READONLY>
682689 *
683690 * buffer.readonly? # => true
684691 *
685692 * buffer.get_string
686693 * # => "test"
687694 *
688695 * buffer.set_string('b', 0)
689- * # ` set_string': Buffer is not writable! (IO::Buffer::AccessError)
696+ * # 'IO::Buffer# set_string': Buffer is not writable! (IO::Buffer::AccessError)
690697 *
691698 * # create read/write mapping: length 4 bytes, offset 0, flags 0
692699 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
@@ -708,31 +715,48 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
708715 // We might like to handle a string path?
709716 VALUE io = argv [0 ];
710717
718+ rb_off_t file_size = rb_file_size (io );
719+ // Compiler can confirm that we handled file_size <= 0 case:
720+ if (UNLIKELY (file_size <= 0 )) {
721+ rb_raise (rb_eArgError , "Invalid negative or zero file size!" );
722+ }
723+ // Here, we assume that file_size is positive:
724+ else if (UNLIKELY ((uintmax_t )file_size > SIZE_MAX )) {
725+ rb_raise (rb_eArgError , "File larger than address space!" );
726+ }
727+
711728 size_t size ;
712729 if (argc >= 2 && !RB_NIL_P (argv [1 ])) {
713730 size = io_buffer_extract_size (argv [1 ]);
714- }
715- else {
716- rb_off_t file_size = rb_file_size (io );
717-
718- // Compiler can confirm that we handled file_size < 0 case:
719- if (file_size < 0 ) {
720- rb_raise (rb_eArgError , "Invalid negative file size!" );
731+ if (UNLIKELY (size == 0 )) {
732+ rb_raise (rb_eArgError , "Size can't be zero!" );
721733 }
722- // Here, we assume that file_size is positive:
723- else if ((uintmax_t )file_size > SIZE_MAX ) {
724- rb_raise (rb_eArgError , "File larger than address space!" );
725- }
726- else {
727- // This conversion should be safe:
728- size = (size_t )file_size ;
734+ if (UNLIKELY (size > (size_t )file_size )) {
735+ rb_raise (rb_eArgError , "Size can't be larger than file size!" );
729736 }
730737 }
738+ else {
739+ // This conversion should be safe:
740+ size = (size_t )file_size ;
741+ }
731742
732743 // This is the file offset, not the buffer offset:
733744 rb_off_t offset = 0 ;
734745 if (argc >= 3 ) {
735746 offset = NUM2OFFT (argv [2 ]);
747+ if (UNLIKELY (offset < 0 )) {
748+ rb_raise (rb_eArgError , "Offset can't be negative!" );
749+ }
750+ if (UNLIKELY (offset >= file_size )) {
751+ rb_raise (rb_eArgError , "Offset too large!" );
752+ }
753+ if (RB_NIL_P (argv [1 ])) {
754+ // Decrease size if it's set from the actual file size:
755+ size = (size_t )(file_size - offset );
756+ }
757+ else if (UNLIKELY ((size_t )(file_size - offset ) < size )) {
758+ rb_raise (rb_eArgError , "Offset too large!" );
759+ }
736760 }
737761
738762 enum rb_io_buffer_flags flags = 0 ;
0 commit comments