@@ -324,6 +324,194 @@ check_decoded(PyObject *decoded)
324324#define SEEN_CRLF 4
325325#define SEEN_ALL (SEEN_CR | SEEN_LF | SEEN_CRLF)
326326
327+ /* Count trailing zero bits; v must not be 0. As in unicodeobject.c. */
328+ #if defined(__clang__ ) || defined(__GNUC__ )
329+ # define HAVE_CTZ 1
330+ static inline unsigned int
331+ ctz (size_t v )
332+ {
333+ return __builtin_ctzll ((unsigned long long )v );
334+ }
335+ #elif defined(_MSC_VER )
336+ # define HAVE_CTZ 1
337+ static inline unsigned int
338+ ctz (size_t v )
339+ {
340+ unsigned long pos ;
341+ # if SIZEOF_SIZE_T == 4
342+ _BitScanForward (& pos , v );
343+ # else
344+ _BitScanForward64 (& pos , v );
345+ # endif
346+ return pos ;
347+ }
348+ #else
349+ # define HAVE_CTZ 0
350+ #endif
351+
352+ /* Skip a size_t word at a time towards the first '\r' or '\n' in the
353+ NUL-terminated UCS1 buffer [s, end). Return a pointer to it, or to an
354+ earlier character from which the caller's own loop finds it.
355+
356+ If out is not NULL, also copy the skipped characters to out. out must
357+ be no further into its buffer than s is into the input, and its buffer
358+ must have room for the whole input. Whole words are copied, so up to
359+ sizeof(size_t) bytes past the skipped characters may also be written;
360+ the caller overwrites them or leaves them past the end of its result.
361+
362+ Lines are often short, and the caller's loop is fastest for them, so if
363+ either of the first two characters is '\r' or below, return a pointer
364+ to it at once. Most words of text have no byte below 0x0E and cost one
365+ cheap test; the rest are checked for '\r' and '\n' exactly, so tabs and
366+ other control characters do not stop the search.
367+
368+ (x - ones * n) & ~x & high flags the bytes of x below n. The cheap test
369+ uses n = 0x0E; the exact test uses n = 1 on x = w ^ (ones * '\n') and
370+ x = w ^ (ones * '\r'), whose zero bytes are the matches. Borrows can
371+ flag extra bytes, but only above a byte below n, so the lowest flagged
372+ byte is always correct: on little-endian machines, ctz() gives the
373+ first match; elsewhere, the caller finds it in the word. */
374+ static inline const char *
375+ skip_to_newline_ucs1 (const char * s , const char * end , char * out )
376+ {
377+ const size_t ones = (size_t )-1 / 0xFF ; /* 0x01 in every byte */
378+ const size_t high = ones * 0x80 ;
379+
380+ /* s[1] is only read when s[0] is not the terminating NUL. */
381+ if ((unsigned char )s [0 ] <= '\r' ) {
382+ return s ;
383+ }
384+ if ((unsigned char )s [1 ] <= '\r' ) {
385+ if (out != NULL ) {
386+ out [0 ] = s [0 ];
387+ }
388+ return s + 1 ;
389+ }
390+ while (end - s >= (Py_ssize_t )sizeof (size_t )) {
391+ size_t w ;
392+ memcpy (& w , s , sizeof (w ));
393+ if (out != NULL ) {
394+ memcpy (out , & w , sizeof (w ));
395+ out += sizeof (w );
396+ }
397+ if ((w - ones * 0x0E ) & ~w & high ) {
398+ size_t lf = w ^ (ones * '\n' );
399+ size_t cr = w ^ (ones * '\r' );
400+ size_t m = (((lf - ones ) & ~lf ) | ((cr - ones ) & ~cr )) & high ;
401+ if (m != 0 ) {
402+ #if PY_LITTLE_ENDIAN && HAVE_CTZ
403+ return s + ctz (m ) / 8 ;
404+ #else
405+ return s ;
406+ #endif
407+ }
408+ }
409+ s += sizeof (w );
410+ }
411+ return s ;
412+ }
413+
414+ /* The universal newlines search of _PyIO_find_line_ending(), for the
415+ NUL-terminated 1-byte string [start, end). */
416+ static Py_ssize_t
417+ find_line_ending_ucs1 (const char * start , const char * end ,
418+ Py_ssize_t * consumed )
419+ {
420+ const char * s = start ;
421+ for (;;) {
422+ unsigned char ch ;
423+ s = skip_to_newline_ucs1 (s , end , NULL );
424+ while ((unsigned char )* s > '\r' ) {
425+ s ++ ;
426+ }
427+ if (s >= end ) {
428+ * consumed = end - start ;
429+ return -1 ;
430+ }
431+ ch = (unsigned char )* s ++ ;
432+ if (ch == '\n' ) {
433+ return s - start ;
434+ }
435+ if (ch == '\r' ) {
436+ return * s == '\n' ? s - start + 1 : s - start ;
437+ }
438+ }
439+ }
440+
441+ /* The newlines scan of _PyIncrementalNewlineDecoder_decode() below, for
442+ the NUL-terminated 1-byte string [s, s + len), len > 0. */
443+ static Py_NO_INLINE int
444+ scan_newlines_ucs1 (const char * s , Py_ssize_t len , int seennl )
445+ {
446+ const char * end = s + len ;
447+ for (;;) {
448+ unsigned char c ;
449+ s = skip_to_newline_ucs1 (s , end , NULL );
450+ while ((unsigned char )* s > '\r' ) {
451+ s ++ ;
452+ }
453+ c = (unsigned char )* s ++ ;
454+ if (c == '\n' ) {
455+ seennl |= SEEN_LF ;
456+ }
457+ else if (c == '\r' ) {
458+ if (* s == '\n' ) {
459+ seennl |= SEEN_CRLF ;
460+ s ++ ;
461+ }
462+ else {
463+ seennl |= SEEN_CR ;
464+ }
465+ }
466+ if (s >= end || seennl == SEEN_ALL ) {
467+ return seennl ;
468+ }
469+ }
470+ }
471+
472+ /* The translation loop of _PyIncrementalNewlineDecoder_decode() below,
473+ for the NUL-terminated 1-byte string [in, in + len). Write the result
474+ to out, which has room for len characters, and return its length. */
475+ static Py_NO_INLINE Py_ssize_t
476+ translate_newlines_ucs1 (const char * in , Py_ssize_t len , char * out ,
477+ int * seennl )
478+ {
479+ const char * s = in , * end = in + len ;
480+ char * o = out ;
481+ int seen = * seennl ;
482+ for (;;) {
483+ unsigned char c ;
484+ const char * p = skip_to_newline_ucs1 (s , end , o );
485+ o += p - s ;
486+ s = p ;
487+ while ((c = (unsigned char )* s ++ ) > '\r' ) {
488+ * o ++ = (char )c ;
489+ }
490+ if (c == '\n' ) {
491+ * o ++ = '\n' ;
492+ seen |= SEEN_LF ;
493+ continue ;
494+ }
495+ if (c == '\r' ) {
496+ if (* s == '\n' ) {
497+ s ++ ;
498+ seen |= SEEN_CRLF ;
499+ }
500+ else {
501+ seen |= SEEN_CR ;
502+ }
503+ * o ++ = '\n' ;
504+ continue ;
505+ }
506+ if (s > end ) {
507+ break ;
508+ }
509+ * o ++ = (char )c ;
510+ }
511+ * seennl = seen ;
512+ return o - out ;
513+ }
514+
327515PyObject *
328516_PyIncrementalNewlineDecoder_decode (PyObject * myself ,
329517 PyObject * input , int final )
@@ -440,6 +628,10 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself,
440628 /* We have already seen all newline types, no need to scan again */
441629 if (seennl == SEEN_ALL )
442630 goto endscan ;
631+ if (kind == PyUnicode_1BYTE_KIND ) {
632+ seennl = scan_newlines_ucs1 (in_str , len , seennl );
633+ goto endscan ;
634+ }
443635 for (;;) {
444636 Py_UCS4 c ;
445637 /* Fast loop for non-control characters */
@@ -481,6 +673,11 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself,
481673 goto error ;
482674 }
483675 in = out = 0 ;
676+ if (kind == PyUnicode_1BYTE_KIND ) {
677+ out = translate_newlines_ucs1 (in_str , len , translated ,
678+ & seennl );
679+ goto translated_all ;
680+ }
484681 for (;;) {
485682 Py_UCS4 c ;
486683 /* Fast loop for non-control characters */
@@ -505,6 +702,7 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself,
505702 break ;
506703 PyUnicode_WRITE (kind , translated , out ++ , c );
507704 }
705+ translated_all :
508706 Py_DECREF (output );
509707 output = PyUnicode_FromKindAndData (kind , translated , out );
510708 PyMem_Free (translated );
@@ -2385,12 +2583,21 @@ _textiowrapper_readline(textio *self, Py_ssize_t limit)
23852583 line_len = PyUnicode_GET_LENGTH (line );
23862584 kind = PyUnicode_KIND (line );
23872585
2388- endpos = _PyIO_find_line_ending (
2389- self -> readtranslate , self -> readuniversal , self -> readnl ,
2390- kind ,
2391- ptr + kind * start ,
2392- ptr + kind * line_len ,
2393- & consumed );
2586+ if (kind == PyUnicode_1BYTE_KIND
2587+ && self -> readuniversal && !self -> readtranslate )
2588+ {
2589+ /* newline='' */
2590+ endpos = find_line_ending_ucs1 (ptr + start , ptr + line_len ,
2591+ & consumed );
2592+ }
2593+ else {
2594+ endpos = _PyIO_find_line_ending (
2595+ self -> readtranslate , self -> readuniversal , self -> readnl ,
2596+ kind ,
2597+ ptr + kind * start ,
2598+ ptr + kind * line_len ,
2599+ & consumed );
2600+ }
23942601 if (endpos >= 0 ) {
23952602 endpos += start ;
23962603 if (limit >= 0 && (endpos - start ) + chunked >= limit )
0 commit comments