@@ -990,12 +990,17 @@ struct Buffered
990990 {
991991 if (auto err = check_initialized (); err.is_err ()) return Err (err.unwrap_err ());
992992
993- if (n < -1 ) { return Err (value_error (" read length must be non-negative or -1" )); }
993+ if (n < 0 ) {
994+ // TODO: determine actual buffer size
995+ n = 4096 ;
996+ }
994997
995998 if (is_closed ()) { return Err (value_error (" read of closed file" )); }
996999
1000+ if (n == 0 ) { return PyBytes::create (); }
1001+
9971002 const auto have = readahead ();
998- if (have > 0 ) { return static_cast <T *>(this )->readfast (n ); }
1003+ if (have > 0 ) { return static_cast <T *>(this )->readfast (std::min< int64_t >(n, have) ); }
9991004 Bytes b;
10001005 b.b .resize (n);
10011006 auto result = static_cast <T *>(this )->raw_read (std::span{ b.b .begin (), b.b .end () });
@@ -1150,8 +1155,8 @@ class BufferedReader
11501155 if (static_cast <int64_t >(n) <= current_size) {
11511156 ASSERT (buffer);
11521157 std::vector<std::byte> data;
1153- data.resize (current_size );
1154- buffer->sgetn (::bit_cast<char *>(data.begin ().base ()), current_size );
1158+ data.resize (n );
1159+ buffer->sgetn (::bit_cast<char *>(data.begin ().base ()), n );
11551160 return PyBytes::create (Bytes{ std::move (data) });
11561161 }
11571162 return Ok (py_none ());
@@ -2207,16 +2212,14 @@ class FileIO : public RawIOBase
22072212 {
22082213 if (!m_filebuffer.is_open ()) { return Err (value_error (" I/O operation on closed file" )); }
22092214
2210- m_filebuffer.pubseekpos (0 , std::ios::in);
2211-
22122215 // TODO: if (m_filestream.fail()) { TODO(); }
22132216 const auto initial_position = m_filebuffer.pubseekoff (0 , std::ios::cur, std::ios::in);
22142217 if (initial_position == -1 ) { TODO (); }
22152218
22162219 const auto end_position = m_filebuffer.pubseekoff (0 , std::ios::end, std::ios::in);
22172220 if (end_position == -1 ) { TODO (); }
22182221
2219- m_filebuffer.pubseekpos (0 , std::ios::in);
2222+ m_filebuffer.pubseekpos (initial_position , std::ios::in);
22202223 // TODO: if (m_filestream.fail()) { TODO(); }
22212224
22222225 const auto file_size = end_position - initial_position;
@@ -3118,45 +3121,58 @@ class TextIOWrapper : public TextIOBase
31183121 std::numeric_limits<size_t >::max ());
31193122 if (result.is_err ()) { return Err (result.unwrap_err ()); }
31203123 auto [limit] = result.unwrap ();
3124+ return readline_impl (limit);
3125+ }
31213126
3127+ PyResult<PyObject *> readline_impl (size_t limit)
3128+ {
31223129 if (auto result = writeflush (); result.is_err ()) { return Err (result.unwrap_err ()); }
31233130
31243131 // Lines in the input can end in \'\\n\', \'\\r\', or \'\\r\\n\'
31253132 auto is_line_end = [](std::string_view remaining) {
3126- if (remaining.starts_with (" \n " ) || remaining.starts_with (" \r " )) {
3127- return 1 ;
3128- } else if (remaining.starts_with (" \r\n " )) {
3129- return 2 ;
3130- }
3133+ if (remaining.starts_with (" \r\n " )) { return 2 ; }
3134+ if (remaining.starts_with (" \n " ) || remaining.starts_with (" \r " )) { return 1 ; }
31313135 return 0 ;
31323136 };
3133- while (!m_buffer_bytes.has_value () || m_buffer_bytes->b .empty ()) {
3134- auto chunk = read_chunk (0 );
3135- if (chunk.is_err ()) { return Err (chunk.unwrap_err ()); }
3136- if (chunk.unwrap () == 0 ) { break ; }
3137- }
3138-
3139- std::string_view remaining{ bit_cast<const char *>(m_buffer_bytes->b .data ()) + m_position,
3140- m_buffer_bytes->b .size () - m_position };
3141-
3142- // TODO: use utf8 codepoints
3143- const char *start = remaining.data ();
3144- size_t len = 0 ;
3145- limit = std::min (limit, remaining.size ());
3146- for (; len < limit; ++len) {
3147- if (auto chars = is_line_end (remaining)) {
3148- // TODO: what if len becomes greater than limit due to \r\n?
3149- len += chars;
3150- break ;
3137+ std::string line;
3138+ bool found_nl = false ;
3139+ while (!found_nl && line.size () < limit) {
3140+ if (!m_buffer_bytes.has_value () || m_buffer_bytes->b .empty ()) {
3141+ auto chunk = read_chunk (0 );
3142+ if (chunk.is_err ()) { return Err (chunk.unwrap_err ()); }
3143+ if (!chunk.unwrap ()) { break ; }
3144+ }
3145+
3146+ // a trailing '\r' may be the first byte of a "\r\n" that straddles the
3147+ // chunk boundary; read ahead so is_line_end can see the full terminator
3148+ while (m_buffer_bytes->b .back () == std::byte{ ' \r ' }) {
3149+ auto chunk = read_chunk (0 );
3150+ if (chunk.is_err ()) { return Err (chunk.unwrap_err ()); }
3151+ if (!chunk.unwrap ()) { break ; }
31513152 }
3152- remaining = remaining.substr (1 );
3153- }
3154- std::string line{ start, start + len };
3155- std::rotate (
3156- m_buffer_bytes->b .begin (), m_buffer_bytes->b .begin () + len, m_buffer_bytes->b .end ());
3157- m_position = 0 ;
3158- m_buffer_bytes->b .resize (m_buffer_bytes->b .size () - line.size ());
31593153
3154+ std::string_view remaining{ bit_cast<const char *>(m_buffer_bytes->b .data ())
3155+ + m_position,
3156+ m_buffer_bytes->b .size () - m_position };
3157+
3158+ // TODO: use utf8 codepoints
3159+ const char *start = remaining.data ();
3160+ size_t len = 0 ;
3161+ const size_t scan_limit = std::min (limit - line.size (), remaining.size ());
3162+ for (; len < scan_limit; ++len) {
3163+ if (auto chars = is_line_end (remaining)) {
3164+ // TODO: what if len becomes greater than limit due to \r\n?
3165+ len += chars;
3166+ found_nl = true ;
3167+ break ;
3168+ }
3169+ remaining = remaining.substr (1 );
3170+ }
3171+ line.insert (line.end (), start, start + len);
3172+ m_buffer_bytes->b .erase (
3173+ m_buffer_bytes->b .begin (), m_buffer_bytes->b .begin () + m_position + len);
3174+ m_position = 0 ;
3175+ }
31603176 return PyString::create (std::move (line));
31613177 }
31623178
@@ -3167,7 +3183,7 @@ class TextIOWrapper : public TextIOBase
31673183 if (result_.is_err ()) { return result_; }
31683184 auto *result = result_.unwrap ();
31693185 while (m_position < m_buffer_bytes->b .size ()) {
3170- auto line = readline ( nullptr , nullptr );
3186+ auto line = readline_impl (std::numeric_limits< size_t >:: max () );
31713187 if (line.is_err ()) { return line; }
31723188 if (auto appended = result->append (line.unwrap ()); appended.is_err ()) {
31733189 return Err (appended.unwrap_err ());
0 commit comments