5151import org .jruby .ast .util .ArgsUtil ;
5252import org .jruby .common .IRubyWarnings .ID ;
5353import org .jruby .exceptions .RaiseException ;
54- import org .jruby .runtime .Block ;
5554import org .jruby .runtime .ThreadContext ;
5655import org .jruby .runtime .builtin .IRubyObject ;
5756import org .jruby .util .ByteList ;
6261import static org .jruby .runtime .Visibility .PRIVATE ;
6362
6463/**
65- * @author kscott
64+ * JRuby implementation of the strscan library from Ruby.
65+ *
66+ * Original implementation by Kelly Nawrocke. Currently a loose port of the C implementation from CRuby.
6667 */
6768@ JRubyClass (name = "StringScanner" )
6869public class RubyStringScanner extends RubyObject {
70+ private static final long serialVersionUID = -3722138049229128675L ;
6971
7072 private RubyString str ;
7173 private int curr = 0 ;
7274 private int prev = -1 ;
7375
74- private Region regs ;
75- private Regex pattern ;
76- private int scannerFlags ;
76+ private transient Region regs ;
77+ private transient Regex pattern ;
78+ private boolean matched ;
7779 private boolean fixedAnchor ;
7880
7981 private static final int MATCHED_STR_SCN_F = 1 << 11 ;
@@ -102,15 +104,15 @@ public static RubyClass createScannerClass(final Ruby runtime) {
102104 }
103105
104106 private void clearMatched () {
105- scannerFlags &= ~ MATCHED_STR_SCN_F ;
107+ matched = false ;
106108 }
107109
108110 private void setMatched () {
109- scannerFlags |= MATCHED_STR_SCN_F ;
111+ matched = true ;
110112 }
111113
112114 private boolean isMatched () {
113- return ( scannerFlags & MATCHED_STR_SCN_F ) != 0 ;
115+ return matched ;
114116 }
115117
116118 private void check (ThreadContext context ) {
@@ -151,7 +153,7 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject other) {
151153 str = otherScanner .str ;
152154 curr = otherScanner .curr ;
153155 prev = otherScanner .prev ;
154- scannerFlags = otherScanner .scannerFlags ;
156+ matched = otherScanner .matched ;
155157
156158 regs = otherScanner .regs .clone ();
157159 pattern = otherScanner .pattern ;
@@ -238,12 +240,13 @@ public IRubyObject charpos(ThreadContext context) {
238240 }
239241
240242 private IRubyObject extractRange (Ruby runtime , int beg , int end ) {
241- int size = str .getByteList ().getRealSize ();
243+ ByteList byteList = str .getByteList ();
244+ int size = byteList .getRealSize ();
242245
243246 if (beg > size ) return runtime .getNil ();
244247 if (end > size ) end = size ;
245248
246- return str . makeSharedString (runtime , beg , end - beg );
249+ return newString (runtime , beg , end - beg );
247250 }
248251
249252 private IRubyObject extractBegLen (Ruby runtime , int beg , int len ) {
@@ -254,23 +257,9 @@ private IRubyObject extractBegLen(Ruby runtime, int beg, int len) {
254257 if (beg > size ) return runtime .getNil ();
255258 len = Math .min (len , size - beg );
256259
257- return str . makeSharedString (runtime , beg , len );
260+ return newString (runtime , beg , len );
258261 }
259262
260- final ThreadLocal <Matcher > currentMatcher = new ThreadLocal <>();
261- final RubyThread .Task <RubyStringScanner , Integer > task = new RubyThread .Task <RubyStringScanner , Integer >() {
262- @ Override
263- public Integer run (ThreadContext context , RubyStringScanner rubyStringScanner ) throws InterruptedException {
264- ByteList value = str .getByteList ();
265- return currentMatcher .get ().matchInterruptible (value .begin () + curr , value .begin () + value .realSize (), Option .NONE );
266- }
267-
268- @ Override
269- public void wakeup (RubyThread thread , RubyStringScanner rubyStringScanner ) {
270- thread .getNativeThread ().interrupt ();
271- }
272- };
273-
274263 // MRI: strscan_do_scan
275264 private IRubyObject scan (ThreadContext context , IRubyObject regex , boolean succptr , boolean getstr , boolean headonly ) {
276265 final Ruby runtime = context .runtime ;
@@ -532,8 +521,9 @@ public IRubyObject peek(ThreadContext context, IRubyObject length) {
532521 }
533522
534523 ByteList value = str .getByteList ();
535- if (curr >= value .getRealSize ()) return RubyString .newEmptyString (context .runtime );
536- if (curr + len > value .getRealSize ()) len = value .getRealSize () - curr ;
524+ int realSize = value .getRealSize ();
525+ if (curr >= realSize ) return RubyString .newEmptyString (context .runtime );
526+ if (curr + len > realSize ) len = realSize - curr ;
537527
538528 return extractBegLen (context .runtime , curr , len );
539529 }
@@ -554,7 +544,7 @@ public IRubyObject unscan(ThreadContext context) {
554544 if (!isMatched ()) {
555545 Ruby runtime = context .runtime ;
556546
557- RubyClass errorClass = runtime . getClass ( "StringScanner" ). getClass ( " Error" );
547+ RubyClass errorClass = ( RubyClass ) runtime . getClassFromPath ( "StringScanner:: Error" );
558548 throw RaiseException .from (runtime , errorClass , "unscan failed: previous match had failed" );
559549 }
560550
@@ -623,7 +613,7 @@ public IRubyObject matchedsize(ThreadContext context) {
623613 if (runtime .isVerbose ()) {
624614 runtime .getWarnings ().warning (ID .DEPRECATED_METHOD , "StringScanner#matchedsize is obsolete; use #matched_size instead" );
625615 }
626- return matched_size ();
616+ return matched_size (context );
627617 }
628618
629619 @ JRubyMethod (name = "[]" )
@@ -686,12 +676,13 @@ public IRubyObject rest(ThreadContext context) {
686676 Ruby runtime = context .runtime ;
687677
688678 ByteList value = str .getByteList ();
679+ int realSize = value .getRealSize ();
689680
690- if (curr >= value . getRealSize () ) {
691- return RubyString .newEmptyString (runtime );
681+ if (curr >= realSize ) {
682+ return RubyString .newEmptyString (runtime , str . getEncoding () );
692683 }
693684
694- return extractRange (runtime , curr , value . getRealSize () );
685+ return extractRange (runtime , curr , realSize );
695686 }
696687
697688 @ JRubyMethod (name = "rest_size" )
@@ -700,10 +691,11 @@ public RubyFixnum rest_size(ThreadContext context) {
700691 Ruby runtime = context .runtime ;
701692
702693 ByteList value = str .getByteList ();
694+ int realSize = value .getRealSize ();
703695
704- if (curr >= value . getRealSize () ) return RubyFixnum .zero (runtime );
696+ if (curr >= realSize ) return RubyFixnum .zero (runtime );
705697
706- return RubyFixnum .newFixnum (runtime , value . getRealSize () - curr );
698+ return RubyFixnum .newFixnum (runtime , realSize - curr );
707699 }
708700
709701 @ JRubyMethod (name = "restsize" )
@@ -719,9 +711,15 @@ public RubyFixnum restsize(ThreadContext context) {
719711 @ Override
720712 public IRubyObject inspect () {
721713 if (str == null ) return inspect ("(uninitialized)" );
722- if (curr >= str .getByteList ().getRealSize ()) return inspect ("fin" );
723- if (curr == 0 ) return inspect (curr + "/" + str .getByteList ().getRealSize () + " @ " + inspect2 ());
724- return inspect (curr + "/" + str .getByteList ().getRealSize () + " " + inspect1 () + " @ " + inspect2 ());
714+
715+ ByteList byteList = str .getByteList ();
716+ int realSize = byteList .getRealSize ();
717+
718+ if (curr >= realSize ) return inspect ("fin" );
719+
720+ if (curr == 0 ) return inspect (curr + "/" + realSize + " @ " + inspect2 ());
721+
722+ return inspect (curr + "/" + realSize + " " + inspect1 () + " @ " + inspect2 ());
725723 }
726724
727725 @ JRubyMethod (name = "fixed_anchor?" )
@@ -766,20 +764,30 @@ private IRubyObject inspect(String msg) {
766764
767765 private IRubyObject inspect1 () {
768766 final Ruby runtime = getRuntime ();
767+
769768 if (curr == 0 ) return RubyString .newEmptyString (runtime );
769+
770770 if (curr > INSPECT_LENGTH ) {
771771 return RubyString .newStringNoCopy (runtime , DOT_BYTES ).append (str .substr (runtime , curr - INSPECT_LENGTH , INSPECT_LENGTH )).inspect ();
772772 }
773+
773774 return str .substr (runtime , 0 , curr ).inspect ();
774775 }
775776
776777 private IRubyObject inspect2 () {
777778 final Ruby runtime = getRuntime ();
778- if (curr >= str .getByteList ().getRealSize ()) return RubyString .newEmptyString (runtime );
779- int len = str .getByteList ().getRealSize () - curr ;
779+
780+ ByteList byteList = str .getByteList ();
781+ int realSize = byteList .getRealSize ();
782+
783+ if (curr >= realSize ) return RubyString .newEmptyString (runtime );
784+
785+ int len = realSize - curr ;
786+
780787 if (len > INSPECT_LENGTH ) {
781788 return ((RubyString ) str .substr (runtime , curr , INSPECT_LENGTH )).cat (DOT_BYTES ).inspect ();
782789 }
790+
783791 return str .substr (runtime , curr , len ).inspect ();
784792 }
785793
@@ -792,7 +800,7 @@ public IRubyObject size(ThreadContext context) {
792800 @ JRubyMethod (name = "captures" )
793801 public IRubyObject captures (ThreadContext context ) {
794802 int i , numRegs ;
795- RubyArray newAry ;
803+ RubyArray <?> newAry ;
796804
797805 if (!isMatched ()) return context .nil ;
798806
@@ -802,9 +810,14 @@ public IRubyObject captures(ThreadContext context) {
802810 newAry = RubyArray .newArray (runtime , numRegs );
803811
804812 for (i = 1 ; i < numRegs ; i ++) {
805- IRubyObject str = extractRange (runtime ,
813+ IRubyObject str ;
814+ if (regs .getBeg (i ) == -1 ) {
815+ str = context .nil ;
816+ } else {
817+ str = extractRange (runtime ,
806818 adjustRegisterPosition (regs .getBeg (i )),
807819 adjustRegisterPosition (regs .getEnd (i )));
820+ }
808821 newAry .push (str );
809822 }
810823
@@ -814,7 +827,7 @@ public IRubyObject captures(ThreadContext context) {
814827 @ JRubyMethod (name = "values_at" , rest = true )
815828 public IRubyObject values_at (ThreadContext context , IRubyObject [] args ) {
816829 int i ;
817- RubyArray newAry ;
830+ RubyArray <?> newAry ;
818831
819832 if (!isMatched ()) return context .nil ;
820833
@@ -828,65 +841,42 @@ public IRubyObject values_at(ThreadContext context, IRubyObject[] args) {
828841 return newAry ;
829842 }
830843
831- @ Deprecated
832- public IRubyObject initialize (IRubyObject [] args , Block unusedBlock ) {
833- str = args [0 ].convertToString ();
834- return this ;
835- }
836-
837- @ Deprecated
838- public IRubyObject initialize_copy (IRubyObject other ) {
839- return initialize_copy (getRuntime ().getCurrentContext (), other );
840- }
844+ @ JRubyMethod (name = "values_at" )
845+ public IRubyObject values_at (ThreadContext context ) {
846+ if (!isMatched ()) return context .nil ;
841847
842- @ Deprecated
843- public IRubyObject concat (IRubyObject obj ) {
844- return concat (getRuntime ().getCurrentContext (), obj );
848+ return RubyArray .newEmptyArray (context .runtime );
845849 }
846850
847- @ Deprecated
848- public RubyFixnum pos () {
849- return pos (getRuntime ().getCurrentContext ());
850- }
851+ @ JRubyMethod (name = "values_at" )
852+ public IRubyObject values_at (ThreadContext context , IRubyObject index ) {
853+ if (!isMatched ()) return context .nil ;
851854
852- @ Deprecated
853- public IRubyObject set_pos (IRubyObject pos ) {
854- return set_pos (getRuntime ().getCurrentContext (), pos );
855+ return RubyArray .newArray (context .runtime , op_aref (context , index ));
855856 }
856857
857- @ Deprecated
858- public IRubyObject getch19 (ThreadContext context ) {
859- return getch (context );
860- }
858+ @ JRubyMethod (name = "values_at" )
859+ public IRubyObject values_at (ThreadContext context , IRubyObject index1 , IRubyObject index2 ) {
860+ if (!isMatched ()) return context .nil ;
861861
862- @ Deprecated
863- public IRubyObject reset () {
864- return reset (getRuntime ().getCurrentContext ());
862+ return RubyArray .newArray (context .runtime , op_aref (context , index1 ), op_aref (context , index2 ));
865863 }
866864
867- @ Deprecated
868- public IRubyObject unscan () {
869- return unscan (getRuntime ().getCurrentContext ());
870- }
865+ @ JRubyMethod (name = "values_at" )
866+ public IRubyObject values_at (ThreadContext context , IRubyObject index1 , IRubyObject index2 , IRubyObject index3 ) {
867+ if (!isMatched ()) return context .nil ;
871868
872- @ Deprecated
873- public IRubyObject matched_size () {
874- return matched_size (getRuntime ().getCurrentContext ());
869+ return RubyArray .newArray (context .runtime , op_aref (context , index1 ), op_aref (context , index2 ), op_aref (context , index3 ));
875870 }
876871
877- @ Deprecated
878- public IRubyObject bol_p ( ) {
879- return bol_p ( getRuntime (). getCurrentContext () );
880- }
872+ // MRI: str_new
873+ private RubyString newString ( Ruby runtime , int start , int length ) {
874+ ByteList byteList = str . getByteList ( );
875+ int begin = byteList . begin ();
881876
882- @ Deprecated
883- public RubyFixnum rest_size () {
884- return rest_size (getRuntime ().getCurrentContext ());
885- }
877+ ByteList newByteList = new ByteList (byteList .unsafeBytes (), begin + start , begin + length , byteList .getEncoding (), true );
886878
887- @ Deprecated
888- public IRubyObject getchCommon (ThreadContext context , boolean is1_9 ) {
889- return getchCommon (context );
879+ return RubyString .newString (runtime , newByteList );
890880 }
891881
892882 /**
0 commit comments