From 0b04d3f279da9288d9e58df9ed7f97c0bb99505f Mon Sep 17 00:00:00 2001
From: John Benediktsson <mrjbq7@gmail.com>
Date: Mon, 24 Feb 2014 21:47:49 -0800
Subject: [PATCH] io.streams.string: faster M\ string-reader stream-readln.

---
 basis/io/streams/string/string-tests.factor |  8 +++++
 basis/io/streams/string/string.factor       | 33 +++++++++++++--------
 2 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/basis/io/streams/string/string-tests.factor b/basis/io/streams/string/string-tests.factor
index fe01626dec..bf20c7452e 100644
--- a/basis/io/streams/string/string-tests.factor
+++ b/basis/io/streams/string/string-tests.factor
@@ -11,6 +11,14 @@ IN: io.streams.string.tests
 ]
 unit-test
 
+{ { "line 1" "line 2" "line 3" } } [
+    "line 1\nline 2\nline 3" <string-reader> stream-lines
+] unit-test
+
+{ { "" "foo" "bar" "baz" } } [
+    "\rfoo\r\nbar\rbaz\n" <string-reader> stream-lines
+] unit-test
+
 [ f ]
 [ "" <string-reader> stream-readln ]
 unit-test
diff --git a/basis/io/streams/string/string.factor b/basis/io/streams/string/string.factor
index f27e308326..7e6042756f 100644
--- a/basis/io/streams/string/string.factor
+++ b/basis/io/streams/string/string.factor
@@ -1,9 +1,7 @@
 ! Copyright (C) 2003, 2009 Slava Pestov, Daniel Ehrenberg.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors io kernel math namespaces sequences sbufs
-strings generic splitting continuations destructors sequences.private
-io.streams.plain io.encodings math.order growable io.streams.sequence
-io.private ;
+USING: accessors destructors io io.encodings io.streams.sequence
+kernel math sbufs sequences sequences.private strings ;
 IN: io.streams.string
 
 ! Readers
@@ -11,28 +9,37 @@ TUPLE: string-reader { underlying string read-only } { i array-capacity } ;
 INSTANCE: string-reader input-stream
 
 M: string-reader stream-element-type drop +character+ ; inline
-M: string-reader stream-read-unsafe sequence-read-unsafe ;
+
+M: string-reader stream-read-unsafe
+    [ integer>fixnum ]
+    [ dup string? [ "not a string" throw ] unless ]
+    [ sequence-read-unsafe ] tri* ;
 M: string-reader stream-read1 sequence-read1 ;
-M: string-reader stream-read-until sequence-read-until ;
+M: string-reader stream-read-until
+    dup >sequence-stream< bounds-check?
+    [ sequence-read-until ] [ 2drop f f ] if ;
+M: string-reader stream-readln
+    dup >sequence-stream< bounds-check? [
+        "\r\n" over sequence-read-until CHAR: \r eq? [
+            over >sequence-stream< dupd ?nth CHAR: \n eq?
+            [ 1 + pick i<< ] [ drop ] if
+        ] when nip "" or
+    ] [ drop f ] if ;
+
 M: string-reader stream-tell i>> ;
 M: string-reader stream-seek (stream-seek) ;
 M: string-reader stream-seekable? drop t ; inline
 M: string-reader stream-length underlying>> length ;
 M: string-reader dispose drop ;
 
-<PRIVATE
-SINGLETON: null-encoding
-M: null-encoding decode-char drop stream-read1 ;
-PRIVATE>
-
 : <string-reader> ( str -- stream )
-    0 string-reader boa null-encoding <decoder> ;
+    0 string-reader boa ;
 
 : with-string-reader ( str quot -- )
     [ <string-reader> ] dip with-input-stream ; inline
 
 ! Writers
-M: sbuf stream-element-type drop +character+ ;
+M: sbuf stream-element-type drop +character+ ; inline
 
 : <string-writer> ( -- stream )
     512 <sbuf> ; inline