stringio 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f8291051ef78bfdd817200ff02c25db3e2fa495d9b4a164c9737511d6eadb38
4
- data.tar.gz: 8593995f8dbb66e6a3dc9f2755f97d0cc05e2d363e6cd027cc15a7d46deaf60f
3
+ metadata.gz: 7c1acfcaec0e05165de2f55e7519bc0c5a732d9e00787843daade47fb287726e
4
+ data.tar.gz: 02eda2070ab8b4a9bc125de803c68de0a01845227c5e4f15359931f9fece7bbd
5
5
  SHA512:
6
- metadata.gz: 5060643c29e66ccc88df7a7ab4c19943d89ce3779641b9be7e0cfc61ce79db7c53af06367875b1892b80f31d98be32d10da2590445cb86425b7d89f285a30b5f
7
- data.tar.gz: 1a6dcf694eaa38dd0d939b8db9cb4f29cc96db93ac545f5f06d6354ad5e75f92fb9807f529f54ae6801a3f791e7139db7ad371bf8efd6b7ca34f45f61fb10429
6
+ metadata.gz: f86b9853126abe79c1b801c2854cb606d532617218fa2a26dcc0c55c2c0ef457bee707e1d9ce08e6bfaf93c09411a175d244773b23ad0970c91ad3d068ba5718
7
+ data.tar.gz: 76083aecab40178e6c7296351c6cb66f128188000d925062d210cc938917ad8e8316074843e1fc7da295e67d391b1be2bded0409c076700a8e15a16f9dc8cd23
data/.rdoc_options CHANGED
@@ -1,2 +1,5 @@
1
1
  ---
2
2
  main_page: README.md
3
+ rdoc_include:
4
+ - doc
5
+ op_dir: html
data/NEWS.md CHANGED
@@ -1,5 +1,165 @@
1
1
  # News
2
2
 
3
+ ## 3.2.0 - 2025-12-17
4
+
5
+ ### Improvements
6
+
7
+ * Improved documents.
8
+ * GH-179
9
+ * GH-188
10
+ * GH-189
11
+ * GH-190
12
+ * GH-191
13
+ * GH-192
14
+ * GH-193
15
+ * GH-194
16
+ * Patch by Burdette Lamar
17
+
18
+ ### Thanks
19
+
20
+ * Burdette Lamar
21
+
22
+ ## 3.1.9 - 2025-12-01
23
+
24
+ ### Improvements
25
+
26
+ * [DOC] Tweaks for StringIO#each_line
27
+ * GH-165
28
+
29
+ * [DOC] Doc for StringIO.size
30
+ * GH-175
31
+
32
+ * [DOC] Tweaks for StringIO#fsync
33
+ * GH-173
34
+
35
+ * [DOC] Fix #seek link
36
+ * GH-174
37
+
38
+ * Add a note about chilled string support to 3.1.8 release note
39
+ * GH-180 fixes GH-179
40
+
41
+ ### Fixes
42
+
43
+ * JRuby: Removed use of RubyBasicObject.flags
44
+ * GH-182
45
+
46
+ ### Thanks
47
+
48
+ * Burdette Lamar
49
+
50
+ * Charles Oliver Nutter
51
+
52
+ ## 3.1.8 - 2025-11-12
53
+
54
+ ### Improvements
55
+
56
+ * Improved documents
57
+ * Patch by Burdette Lamar
58
+
59
+ * Improved chilled string support
60
+ * GH-128
61
+
62
+ ### Fixes
63
+
64
+ * Fixed SEGV in `StringIO#seek` with `SEEK_END` on `StringIO.new(nil)`
65
+ * GH-137
66
+ * Patch by koh-sh
67
+
68
+ * Fixed SEGV in `StringIO#read` on `StringIO.new(nil)`
69
+
70
+ * Fixed SEGV in `StringIO#pread` on `StringIO.new(nil)`
71
+
72
+ * Fixed SEGV in `StringIO#eof?` on `StringIO.new(nil)`
73
+
74
+ * JRuby: Fixed a bug that `StringIO#read` doesn't clear code range
75
+ * GH-156
76
+ * Patch by Karol Bucek
77
+
78
+ ### Thanks
79
+
80
+ * koh-sh
81
+
82
+ * Burdette Lamar
83
+
84
+ * Karol Bucek
85
+
86
+ ## 3.1.7 - 2025-04-21
87
+
88
+ ### Improvements
89
+
90
+ * CRuby: Added support for `rb_io_mode_t` that will be introduced in
91
+ Ruby 3.5 or later.
92
+ * GH-129
93
+ * Patch by Samuel Williams
94
+
95
+ ### Thanks
96
+
97
+ * Samuel Williams
98
+
99
+ ## 3.1.6 - 2025-03-25
100
+
101
+ ### Fixes
102
+
103
+ * CRuby: Fix SEGV at unget to a null device StringIO
104
+ * JRuby:
105
+ * Fix NullPointerException at unget to a null device StringIO
106
+ * Use proper checkEncoding signature
107
+ * Update strioWrite logic to match CRuby
108
+ * GH-124
109
+
110
+ ## 3.1.5 - 2025-02-21
111
+
112
+ ### Improvements
113
+
114
+ * JRuby: Improved compatibility with CRuby for `StringIO#seek` with
115
+ frozen string.
116
+ * GH-119
117
+ * GH-121
118
+
119
+ ## 3.1.4 - 2025-02-20
120
+
121
+ ### Improvements
122
+
123
+ * JRuby: Improved compatibility with CRuby.
124
+ * GH-116
125
+
126
+ ### Fixes
127
+
128
+ * CRuby: Fixed a bug that `StringIO` may mutate a shared string.
129
+ * GH-117
130
+
131
+ ## 3.1.3 - 2025-02-14
132
+
133
+ ### Fixes
134
+
135
+ * JRuby: Fixed a bug that JRuby may not be able to be started
136
+ * GH-112
137
+ * GH-113
138
+ * Reported by Karol Bucek
139
+
140
+ ### Thanks
141
+
142
+ * Karol Bucek
143
+
144
+ ## 3.1.2 - 2024-11-07
145
+
146
+ ### Improvements
147
+
148
+ * JRuby: Added support for detecting encoding by BOM.
149
+ * GH-100
150
+ * GH-101
151
+
152
+ ### Fixes
153
+
154
+ * CRuby: Fixed a bug that unknown memory may be used by
155
+ `StringIO#ungetc`/`StringIO#ungetbyte`.
156
+ * https://hackerone.com/reports/2805165
157
+ * Reported by manun.
158
+
159
+ ### Thanks
160
+
161
+ * manun
162
+
3
163
  ## 3.1.1 - 2024-06-13
4
164
 
5
165
  ### Improvements
data/docs/io.rb CHANGED
@@ -1,8 +1,3 @@
1
- # :stopdoc:
1
+ # The {built-in class for I/O}[https://docs.ruby-lang.org/en/master/IO.html].
2
2
  class IO
3
- module generic_readable
4
- end
5
- module generic_writable
6
- end
7
3
  end
8
- # :startdoc:
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: false
2
2
  require 'mkmf'
3
3
  if RUBY_ENGINE == 'ruby'
4
+ have_type("rb_io_mode_t", "ruby/io.h")
5
+
4
6
  create_makefile('stringio')
5
7
  else
6
8
  File.write('Makefile', dummy_makefile("").join)
@@ -13,13 +13,14 @@
13
13
  **********************************************************************/
14
14
 
15
15
  static const char *const
16
- STRINGIO_VERSION = "3.1.1";
16
+ STRINGIO_VERSION = "3.2.0";
17
17
 
18
18
  #include <stdbool.h>
19
19
 
20
20
  #include "ruby.h"
21
21
  #include "ruby/io.h"
22
22
  #include "ruby/encoding.h"
23
+ #include "ruby/version.h"
23
24
  #if defined(HAVE_FCNTL_H) || defined(_WIN32)
24
25
  #include <fcntl.h>
25
26
  #elif defined(HAVE_SYS_FCNTL_H)
@@ -35,12 +36,29 @@ STRINGIO_VERSION = "3.1.1";
35
36
  # define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
36
37
  #endif
37
38
 
39
+ static inline bool
40
+ str_chilled_p(VALUE str)
41
+ {
42
+ #if (RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 4) || RUBY_API_VERSION_MAJOR >= 4
43
+ // Do not attempt to modify chilled strings on Ruby 3.4+
44
+ // RUBY_FL_USER2 == STR_CHILLED_LITERAL
45
+ // RUBY_FL_USER3 == STR_CHILLED_SYMBOL_TO_S
46
+ return FL_TEST_RAW(str, RUBY_FL_USER2 | RUBY_FL_USER3);
47
+ #else
48
+ return false;
49
+ #endif
50
+ }
51
+
52
+ #ifndef HAVE_TYPE_RB_IO_MODE_T
53
+ typedef int rb_io_mode_t;
54
+ #endif
55
+
38
56
  struct StringIO {
39
57
  VALUE string;
40
58
  rb_encoding *enc;
41
59
  long pos;
42
60
  long lineno;
43
- int flags;
61
+ rb_io_mode_t flags;
44
62
  int count;
45
63
  };
46
64
 
@@ -180,6 +198,21 @@ check_modifiable(struct StringIO *ptr)
180
198
  else if (OBJ_FROZEN_RAW(ptr->string)) {
181
199
  rb_raise(rb_eIOError, "not modifiable string");
182
200
  }
201
+ else {
202
+ rb_str_modify(ptr->string);
203
+ }
204
+ }
205
+
206
+ static inline bool
207
+ outside_p(struct StringIO *ptr, long pos)
208
+ {
209
+ return NIL_P(ptr->string) || pos >= RSTRING_LEN(ptr->string);
210
+ }
211
+
212
+ static inline bool
213
+ eos_p(struct StringIO *ptr)
214
+ {
215
+ return outside_p(ptr, ptr->pos);
183
216
  }
184
217
 
185
218
  static VALUE
@@ -192,17 +225,32 @@ strio_s_allocate(VALUE klass)
192
225
  * call-seq:
193
226
  * StringIO.new(string = '', mode = 'r+') -> new_stringio
194
227
  *
195
- * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
196
- *
197
228
  * Returns a new \StringIO instance formed from +string+ and +mode+;
198
- * see {Access Modes}[rdoc-ref:File@Access+Modes]:
229
+ * the instance should be closed when no longer needed:
230
+ *
231
+ * strio = StringIO.new
232
+ * strio.string # => ""
233
+ * strio.closed_read? # => false
234
+ * strio.closed_write? # => false
235
+ * strio.close
236
+ *
237
+ * If +string+ is frozen, the default +mode+ is <tt>'r'</tt>:
199
238
  *
200
- * strio = StringIO.new # => #<StringIO>
239
+ * strio = StringIO.new('foo'.freeze)
240
+ * strio.string # => "foo"
241
+ * strio.closed_read? # => false
242
+ * strio.closed_write? # => true
201
243
  * strio.close
202
244
  *
203
- * The instance should be closed when no longer needed.
245
+ * Argument +mode+ must be a valid
246
+ * {Access Mode}[https://docs.ruby-lang.org/en/master/File.html#class-File-label-Access+Modes],
247
+ * which may be a string or an integer constant:
204
248
  *
205
- * Related: StringIO.open (accepts block; closes automatically).
249
+ * StringIO.new('foo', 'w+')
250
+ * StringIO.new('foo', File::RDONLY)
251
+ *
252
+ * Related: StringIO.open
253
+ * (passes the \StringIO object to the block; closes the object automatically on block exit).
206
254
  */
207
255
  static VALUE
208
256
  strio_initialize(int argc, VALUE *argv, VALUE self)
@@ -337,23 +385,20 @@ strio_finalize(VALUE self)
337
385
 
338
386
  /*
339
387
  * call-seq:
340
- * StringIO.open(string = '', mode = 'r+') {|strio| ... }
341
- *
342
- * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
388
+ * StringIO.open(string = '', mode = 'r+') -> new_stringio
389
+ * StringIO.open(string = '', mode = 'r+') {|strio| ... } -> object
343
390
  *
344
- * Creates a new \StringIO instance formed from +string+ and +mode+;
345
- * see {Access Modes}[rdoc-ref:File@Access+Modes].
391
+ * Creates new \StringIO instance by calling <tt>StringIO.new(string, mode)</tt>.
346
392
  *
347
- * With no block, returns the new instance:
393
+ * With no block given, returns the new instance:
348
394
  *
349
395
  * strio = StringIO.open # => #<StringIO>
350
396
  *
351
- * With a block, calls the block with the new instance
397
+ * With a block given, calls the block with the new instance
352
398
  * and returns the block's value;
353
- * closes the instance on block exit.
399
+ * closes the instance on block exit:
354
400
  *
355
- * StringIO.open {|strio| p strio }
356
- * # => #<StringIO>
401
+ * StringIO.open('foo') {|strio| strio.string.upcase } # => "FOO"
357
402
  *
358
403
  * Related: StringIO.new.
359
404
  */
@@ -379,7 +424,7 @@ strio_s_new(int argc, VALUE *argv, VALUE klass)
379
424
  }
380
425
 
381
426
  /*
382
- * Returns +false+. Just for compatibility to IO.
427
+ * Returns +false+; for compatibility with IO.
383
428
  */
384
429
  static VALUE
385
430
  strio_false(VALUE self)
@@ -389,7 +434,7 @@ strio_false(VALUE self)
389
434
  }
390
435
 
391
436
  /*
392
- * Returns +nil+. Just for compatibility to IO.
437
+ * Returns +nil+; for compatibility with IO.
393
438
  */
394
439
  static VALUE
395
440
  strio_nil(VALUE self)
@@ -399,7 +444,7 @@ strio_nil(VALUE self)
399
444
  }
400
445
 
401
446
  /*
402
- * Returns an object itself. Just for compatibility to IO.
447
+ * Returns +self+; for compatibility with IO.
403
448
  */
404
449
  static VALUE
405
450
  strio_self(VALUE self)
@@ -409,7 +454,7 @@ strio_self(VALUE self)
409
454
  }
410
455
 
411
456
  /*
412
- * Returns 0. Just for compatibility to IO.
457
+ * Returns 0; for compatibility with IO.
413
458
  */
414
459
  static VALUE
415
460
  strio_0(VALUE self)
@@ -469,7 +514,7 @@ strio_get_string(VALUE self)
469
514
  * call-seq:
470
515
  * string = other_string -> other_string
471
516
  *
472
- * Assigns the underlying string as +other_string+, and sets position to zero;
517
+ * Replaces the stored string with +other_string+, and sets the position to zero;
473
518
  * returns +other_string+:
474
519
  *
475
520
  * StringIO.open('foo') do |strio|
@@ -483,7 +528,7 @@ strio_get_string(VALUE self)
483
528
  * "foo"
484
529
  * "bar"
485
530
  *
486
- * Related: StringIO#string (returns the underlying string).
531
+ * Related: StringIO#string (returns the stored string).
487
532
  */
488
533
  static VALUE
489
534
  strio_set_string(VALUE self, VALUE string)
@@ -504,11 +549,16 @@ strio_set_string(VALUE self, VALUE string)
504
549
  * call-seq:
505
550
  * close -> nil
506
551
  *
507
- * Closes +self+ for both reading and writing.
552
+ * Closes +self+ for both reading and writing; returns +nil+:
508
553
  *
509
- * Raises IOError if reading or writing is attempted.
554
+ * strio = StringIO.new
555
+ * strio.closed? # => false
556
+ * strio.close # => nil
557
+ * strio.closed? # => true
558
+ * strio.read # Raises IOError: not opened for reading
559
+ * strio.write # Raises IOError: not opened for writing
510
560
  *
511
- * Related: StringIO#close_read, StringIO#close_write.
561
+ * Related: StringIO#close_read, StringIO#close_write, StringIO.closed?.
512
562
  */
513
563
  static VALUE
514
564
  strio_close(VALUE self)
@@ -522,9 +572,16 @@ strio_close(VALUE self)
522
572
  * call-seq:
523
573
  * close_read -> nil
524
574
  *
525
- * Closes +self+ for reading; closed-write setting remains unchanged.
575
+ * Closes +self+ for reading;
576
+ * closed-write setting remains unchanged;
577
+ * returns +nil+:
526
578
  *
527
- * Raises IOError if reading is attempted.
579
+ * strio = StringIO.new
580
+ * strio.closed_read? # => false
581
+ * strio.close_read # => nil
582
+ * strio.closed_read? # => true
583
+ * strio.closed_write? # => false
584
+ * strio.read # Raises IOError: not opened for reading
528
585
  *
529
586
  * Related: StringIO#close, StringIO#close_write.
530
587
  */
@@ -543,11 +600,16 @@ strio_close_read(VALUE self)
543
600
  * call-seq:
544
601
  * close_write -> nil
545
602
  *
546
- * Closes +self+ for writing; closed-read setting remains unchanged.
603
+ * Closes +self+ for writing; closed-read setting remains unchanged; returns +nil+:
547
604
  *
548
- * Raises IOError if writing is attempted.
605
+ * strio = StringIO.new
606
+ * strio.closed_write? # => false
607
+ * strio.close_write # => nil
608
+ * strio.closed_write? # => true
609
+ * strio.closed_read? # => false
610
+ * strio.write('foo') # Raises IOError: not opened for writing
549
611
  *
550
- * Related: StringIO#close, StringIO#close_read.
612
+ * Related: StringIO#close, StringIO#close_read, StringIO#closed_write?.
551
613
  */
552
614
  static VALUE
553
615
  strio_close_write(VALUE self)
@@ -564,8 +626,16 @@ strio_close_write(VALUE self)
564
626
  * call-seq:
565
627
  * closed? -> true or false
566
628
  *
567
- * Returns +true+ if +self+ is closed for both reading and writing,
568
- * +false+ otherwise.
629
+ * Returns whether +self+ is closed for both reading and writing:
630
+ *
631
+ * strio = StringIO.new
632
+ * strio.closed? # => false # Open for reading and writing.
633
+ * strio.close_read
634
+ * strio.closed? # => false # Still open for writing.
635
+ * strio.close_write
636
+ * strio.closed? # => true # Now closed for both.
637
+ *
638
+ * Related: StringIO.closed_read?, StringIO.closed_write?.
569
639
  */
570
640
  static VALUE
571
641
  strio_closed(VALUE self)
@@ -579,7 +649,14 @@ strio_closed(VALUE self)
579
649
  * call-seq:
580
650
  * closed_read? -> true or false
581
651
  *
582
- * Returns +true+ if +self+ is closed for reading, +false+ otherwise.
652
+ * Returns whether +self+ is closed for reading:
653
+ *
654
+ * strio = StringIO.new
655
+ * strio.closed_read? # => false
656
+ * strio.close_read
657
+ * strio.closed_read? # => true
658
+ *
659
+ * Related: StringIO#closed?, StringIO#closed_write?, StringIO#close_read.
583
660
  */
584
661
  static VALUE
585
662
  strio_closed_read(VALUE self)
@@ -593,7 +670,14 @@ strio_closed_read(VALUE self)
593
670
  * call-seq:
594
671
  * closed_write? -> true or false
595
672
  *
596
- * Returns +true+ if +self+ is closed for writing, +false+ otherwise.
673
+ * Returns whether +self+ is closed for writing:
674
+ *
675
+ * strio = StringIO.new
676
+ * strio.closed_write? # => false
677
+ * strio.close_write
678
+ * strio.closed_write? # => true
679
+ *
680
+ * Related: StringIO#close_write, StringIO#closed?, StringIO#closed_read?.
597
681
  */
598
682
  static VALUE
599
683
  strio_closed_write(VALUE self)
@@ -607,19 +691,26 @@ static struct StringIO *
607
691
  strio_to_read(VALUE self)
608
692
  {
609
693
  struct StringIO *ptr = readable(self);
610
- if (NIL_P(ptr->string)) return NULL;
611
- if (ptr->pos < RSTRING_LEN(ptr->string)) return ptr;
612
- return NULL;
694
+ if (eos_p(ptr)) return NULL;
695
+ return ptr;
613
696
  }
614
697
 
615
698
  /*
616
699
  * call-seq:
617
700
  * eof? -> true or false
618
701
  *
619
- * Returns +true+ if positioned at end-of-stream, +false+ otherwise;
620
- * see {Position}[rdoc-ref:IO@Position].
702
+ * Returns whether +self+ is positioned at end-of-stream:
703
+ *
704
+ * strio = StringIO.new('foo')
705
+ * strio.pos # => 0
706
+ * strio.eof? # => false
707
+ * strio.read # => "foo"
708
+ * strio.pos # => 3
709
+ * strio.eof? # => true
710
+ * strio.close_read
711
+ * strio.eof? # Raises IOError: not opened for reading
621
712
  *
622
- * Raises IOError if the stream is not opened for reading.
713
+ * Related: StringIO#pos.
623
714
  */
624
715
  static VALUE
625
716
  strio_eof(VALUE self)
@@ -656,7 +747,7 @@ strio_copy(VALUE copy, VALUE orig)
656
747
  * lineno -> current_line_number
657
748
  *
658
749
  * Returns the current line number in +self+;
659
- * see {Line Number}[rdoc-ref:IO@Line+Number].
750
+ * see {Line Number}[rdoc-ref:StringIO@Line+Number].
660
751
  */
661
752
  static VALUE
662
753
  strio_get_lineno(VALUE self)
@@ -669,7 +760,7 @@ strio_get_lineno(VALUE self)
669
760
  * lineno = new_line_number -> new_line_number
670
761
  *
671
762
  * Sets the current line number in +self+ to the given +new_line_number+;
672
- * see {Line Number}[rdoc-ref:IO@Line+Number].
763
+ * see {Line Number}[rdoc-ref:StringIO@Line+Number].
673
764
  */
674
765
  static VALUE
675
766
  strio_set_lineno(VALUE self, VALUE lineno)
@@ -683,7 +774,7 @@ strio_set_lineno(VALUE self, VALUE lineno)
683
774
  * binmode -> self
684
775
  *
685
776
  * Sets the data mode in +self+ to binary mode;
686
- * see {Data Mode}[rdoc-ref:File@Data+Mode].
777
+ * see {Data Mode}[rdoc-ref:StringIO@Data+Mode].
687
778
  *
688
779
  */
689
780
  static VALUE
@@ -744,7 +835,7 @@ strio_reopen(int argc, VALUE *argv, VALUE self)
744
835
  * pos -> stream_position
745
836
  *
746
837
  * Returns the current position (in bytes);
747
- * see {Position}[rdoc-ref:IO@Position].
838
+ * see {Position}[rdoc-ref:StringIO@Position].
748
839
  */
749
840
  static VALUE
750
841
  strio_get_pos(VALUE self)
@@ -757,7 +848,7 @@ strio_get_pos(VALUE self)
757
848
  * pos = new_position -> new_position
758
849
  *
759
850
  * Sets the current position (in bytes);
760
- * see {Position}[rdoc-ref:IO@Position].
851
+ * see {Position}[rdoc-ref:StringIO@Position].
761
852
  */
762
853
  static VALUE
763
854
  strio_set_pos(VALUE self, VALUE pos)
@@ -792,9 +883,9 @@ strio_rewind(VALUE self)
792
883
  * call-seq:
793
884
  * seek(offset, whence = SEEK_SET) -> 0
794
885
  *
795
- * Sets the current position to the given integer +offset+ (in bytes),
886
+ * Sets the position to the given integer +offset+ (in bytes),
796
887
  * with respect to a given constant +whence+;
797
- * see {Position}[rdoc-ref:IO@Position].
888
+ * see {IO#seek}[https://docs.ruby-lang.org/en/master/IO.html#method-i-seek].
798
889
  */
799
890
  static VALUE
800
891
  strio_seek(int argc, VALUE *argv, VALUE self)
@@ -816,7 +907,11 @@ strio_seek(int argc, VALUE *argv, VALUE self)
816
907
  offset = ptr->pos;
817
908
  break;
818
909
  case 2:
819
- offset = RSTRING_LEN(ptr->string);
910
+ if (NIL_P(ptr->string)) {
911
+ offset = 0;
912
+ } else {
913
+ offset = RSTRING_LEN(ptr->string);
914
+ }
820
915
  break;
821
916
  default:
822
917
  error_inval("invalid whence");
@@ -849,10 +944,9 @@ strio_get_sync(VALUE self)
849
944
  * call-seq:
850
945
  * each_byte {|byte| ... } -> self
851
946
  *
852
- * With a block given, calls the block with each remaining byte in the stream;
853
- * see {Byte IO}[rdoc-ref:IO@Byte+IO].
947
+ * :include: stringio/each_byte.rdoc
854
948
  *
855
- * With no block given, returns an enumerator.
949
+ * Related: StringIO#each_char, StringIO#each_codepoint, StringIO#each_line.
856
950
  */
857
951
  static VALUE
858
952
  strio_each_byte(VALUE self)
@@ -870,10 +964,10 @@ strio_each_byte(VALUE self)
870
964
 
871
965
  /*
872
966
  * call-seq:
873
- * getc -> character or nil
967
+ * getc -> character, byte, or nil
968
+ *
969
+ * :include: stringio/getc.rdoc
874
970
  *
875
- * Reads and returns the next character from the stream;
876
- * see {Character IO}[rdoc-ref:IO@Character+IO].
877
971
  */
878
972
  static VALUE
879
973
  strio_getc(VALUE self)
@@ -885,7 +979,7 @@ strio_getc(VALUE self)
885
979
  int len;
886
980
  char *p;
887
981
 
888
- if (NIL_P(str) || pos >= RSTRING_LEN(str)) {
982
+ if (eos_p(ptr)) {
889
983
  return Qnil;
890
984
  }
891
985
  p = RSTRING_PTR(str)+pos;
@@ -896,17 +990,17 @@ strio_getc(VALUE self)
896
990
 
897
991
  /*
898
992
  * call-seq:
899
- * getbyte -> byte or nil
993
+ * getbyte -> integer or nil
994
+ *
995
+ * :include: stringio/getbyte.rdoc
900
996
  *
901
- * Reads and returns the next 8-bit byte from the stream;
902
- * see {Byte IO}[rdoc-ref:IO@Byte+IO].
903
997
  */
904
998
  static VALUE
905
999
  strio_getbyte(VALUE self)
906
1000
  {
907
1001
  struct StringIO *ptr = readable(self);
908
1002
  int c;
909
- if (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string)) {
1003
+ if (eos_p(ptr)) {
910
1004
  return Qnil;
911
1005
  }
912
1006
  c = RSTRING_PTR(ptr->string)[ptr->pos++];
@@ -930,6 +1024,18 @@ strio_extend(struct StringIO *ptr, long pos, long len)
930
1024
  }
931
1025
  }
932
1026
 
1027
+ static void
1028
+ strio_unget_string(struct StringIO *ptr, VALUE c)
1029
+ {
1030
+ const char *cp = NULL;
1031
+ long cl = RSTRING_LEN(c);
1032
+ if (cl > 0) {
1033
+ if (c != ptr->string) cp = RSTRING_PTR(c);
1034
+ strio_unget_bytes(ptr, cp, cl);
1035
+ RB_GC_GUARD(c);
1036
+ }
1037
+ }
1038
+
933
1039
  /*
934
1040
  * call-seq:
935
1041
  * ungetc(character) -> nil
@@ -952,19 +1058,22 @@ strio_ungetc(VALUE self, VALUE c)
952
1058
 
953
1059
  enc = rb_enc_get(ptr->string);
954
1060
  len = rb_enc_codelen(cc, enc);
955
- if (len <= 0) rb_enc_uint_chr(cc, enc);
1061
+ if (len <= 0) {
1062
+ rb_enc_uint_chr(cc, enc); /* to raise an exception */
1063
+ UNREACHABLE;
1064
+ }
956
1065
  rb_enc_mbcput(cc, buf, enc);
957
1066
  return strio_unget_bytes(ptr, buf, len);
958
1067
  }
959
1068
  else {
960
- SafeStringValue(c);
1069
+ StringValue(c);
1070
+ if (RSTRING_LEN(c) == 0) return Qnil;
961
1071
  enc = rb_enc_get(ptr->string);
962
1072
  enc2 = rb_enc_get(c);
963
1073
  if (enc != enc2 && enc != rb_ascii8bit_encoding()) {
964
1074
  c = rb_str_conv_enc(c, enc2, enc);
965
1075
  }
966
- strio_unget_bytes(ptr, RSTRING_PTR(c), RSTRING_LEN(c));
967
- RB_GC_GUARD(c);
1076
+ strio_unget_string(ptr, c);
968
1077
  return Qnil;
969
1078
  }
970
1079
  }
@@ -991,13 +1100,8 @@ strio_ungetbyte(VALUE self, VALUE c)
991
1100
  strio_unget_bytes(ptr, &cc, 1);
992
1101
  }
993
1102
  else {
994
- long cl;
995
- SafeStringValue(c);
996
- cl = RSTRING_LEN(c);
997
- if (cl > 0) {
998
- strio_unget_bytes(ptr, RSTRING_PTR(c), cl);
999
- RB_GC_GUARD(c);
1000
- }
1103
+ StringValue(c);
1104
+ strio_unget_string(ptr, c);
1001
1105
  }
1002
1106
  return Qnil;
1003
1107
  }
@@ -1028,7 +1132,7 @@ strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl)
1028
1132
  if (rest > cl) memset(s + len, 0, rest - cl);
1029
1133
  pos -= cl;
1030
1134
  }
1031
- memcpy(s + pos, cp, cl);
1135
+ memcpy(s + pos, (cp ? cp : s), cl);
1032
1136
  ptr->pos = pos;
1033
1137
  return Qnil;
1034
1138
  }
@@ -1065,12 +1169,11 @@ strio_readbyte(VALUE self)
1065
1169
 
1066
1170
  /*
1067
1171
  * call-seq:
1068
- * each_char {|c| ... } -> self
1172
+ * each_char {|char| ... } -> self
1069
1173
  *
1070
- * With a block given, calls the block with each remaining character in the stream;
1071
- * see {Character IO}[rdoc-ref:IO@Character+IO].
1174
+ * :include: stringio/each_char.rdoc
1072
1175
  *
1073
- * With no block given, returns an enumerator.
1176
+ * Related: StringIO#each_byte, StringIO#each_codepoint, StringIO#each_line.
1074
1177
  */
1075
1178
  static VALUE
1076
1179
  strio_each_char(VALUE self)
@@ -1089,10 +1192,9 @@ strio_each_char(VALUE self)
1089
1192
  * call-seq:
1090
1193
  * each_codepoint {|codepoint| ... } -> self
1091
1194
  *
1092
- * With a block given, calls the block with each remaining codepoint in the stream;
1093
- * see {Codepoint IO}[rdoc-ref:IO@Codepoint+IO].
1195
+ * :include: stringio/each_codepoint.rdoc
1094
1196
  *
1095
- * With no block given, returns an enumerator.
1197
+ * Related: StringIO#each_byte, StringIO#each_char, StringIO#each_line.
1096
1198
  */
1097
1199
  static VALUE
1098
1200
  strio_each_codepoint(VALUE self)
@@ -1326,9 +1428,8 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
1326
1428
  * gets(limit, chomp: false) -> string or nil
1327
1429
  * gets(sep, limit, chomp: false) -> string or nil
1328
1430
  *
1329
- * Reads and returns a line from the stream;
1330
- * assigns the return value to <tt>$_</tt>;
1331
- * see {Line IO}[rdoc-ref:IO@Line+IO].
1431
+ * :include: stringio/gets.rdoc
1432
+ *
1332
1433
  */
1333
1434
  static VALUE
1334
1435
  strio_gets(int argc, VALUE *argv, VALUE self)
@@ -1365,15 +1466,15 @@ strio_readline(int argc, VALUE *argv, VALUE self)
1365
1466
  }
1366
1467
 
1367
1468
  /*
1469
+ * :markup: markdown
1470
+ *
1368
1471
  * call-seq:
1369
1472
  * each_line(sep = $/, chomp: false) {|line| ... } -> self
1370
1473
  * each_line(limit, chomp: false) {|line| ... } -> self
1371
1474
  * each_line(sep, limit, chomp: false) {|line| ... } -> self
1372
1475
  *
1373
- * Calls the block with each remaining line read from the stream;
1374
- * does nothing if already at end-of-file;
1375
- * returns +self+.
1376
- * See {Line IO}[rdoc-ref:IO@Line+IO].
1476
+ * :include: stringio/each_line.md
1477
+ *
1377
1478
  */
1378
1479
  static VALUE
1379
1480
  strio_each(int argc, VALUE *argv, VALUE self)
@@ -1574,10 +1675,9 @@ strio_read(int argc, VALUE *argv, VALUE self)
1574
1675
  if (len < 0) {
1575
1676
  rb_raise(rb_eArgError, "negative length %ld given", len);
1576
1677
  }
1577
- if (len > 0 &&
1578
- (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string))) {
1678
+ if (eos_p(ptr)) {
1579
1679
  if (!NIL_P(str)) rb_str_resize(str, 0);
1580
- return Qnil;
1680
+ return len > 0 ? Qnil : rb_str_new(0, 0);
1581
1681
  }
1582
1682
  binary = 1;
1583
1683
  break;
@@ -1653,7 +1753,7 @@ strio_pread(int argc, VALUE *argv, VALUE self)
1653
1753
 
1654
1754
  struct StringIO *ptr = readable(self);
1655
1755
 
1656
- if (offset >= RSTRING_LEN(ptr->string)) {
1756
+ if (outside_p(ptr, offset)) {
1657
1757
  rb_eof_error();
1658
1758
  }
1659
1759
 
@@ -1743,10 +1843,10 @@ strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self)
1743
1843
 
1744
1844
  /*
1745
1845
  * call-seq:
1746
- * strio.length -> integer
1747
- * strio.size -> integer
1846
+ * size -> integer
1847
+ *
1848
+ * :include: stringio/size.rdoc
1748
1849
  *
1749
- * Returns the size of the buffer string.
1750
1850
  */
1751
1851
  static VALUE
1752
1852
  strio_size(VALUE self)
@@ -1784,12 +1884,20 @@ strio_truncate(VALUE self, VALUE len)
1784
1884
  }
1785
1885
 
1786
1886
  /*
1787
- * call-seq:
1788
- * strio.external_encoding => encoding
1887
+ * call-seq:
1888
+ * external_encoding -> encoding or nil
1889
+ *
1890
+ * Returns an Encoding object that represents the encoding of the string;
1891
+ * see {Encodings}[rdoc-ref:StringIO@Encodings]:
1892
+ *
1893
+ * strio = StringIO.new('foo')
1894
+ * strio.external_encoding # => #<Encoding:UTF-8>
1895
+ *
1896
+ * Returns +nil+ if +self+ has no string and is in write mode:
1897
+ *
1898
+ * strio = StringIO.new(nil, 'w+')
1899
+ * strio.external_encoding # => nil
1789
1900
  *
1790
- * Returns the Encoding object that represents the encoding of the file.
1791
- * If the stream is write mode and no encoding is specified, returns
1792
- * +nil+.
1793
1901
  */
1794
1902
 
1795
1903
  static VALUE
@@ -1801,10 +1909,9 @@ strio_external_encoding(VALUE self)
1801
1909
 
1802
1910
  /*
1803
1911
  * call-seq:
1804
- * strio.internal_encoding => encoding
1912
+ * internal_encoding -> nil
1805
1913
  *
1806
- * Returns the Encoding of the internal string if conversion is
1807
- * specified. Otherwise returns +nil+.
1914
+ * Returns +nil+; for compatibility with IO.
1808
1915
  */
1809
1916
 
1810
1917
  static VALUE
@@ -1839,14 +1946,15 @@ strio_set_encoding(int argc, VALUE *argv, VALUE self)
1839
1946
  enc = rb_find_encoding(ext_enc);
1840
1947
  if (!enc) {
1841
1948
  rb_io_enc_t convconfig;
1842
- int oflags, fmode;
1949
+ int oflags;
1950
+ rb_io_mode_t fmode;
1843
1951
  VALUE vmode = rb_str_append(rb_str_new_cstr("r:"), ext_enc);
1844
1952
  rb_io_extract_modeenc(&vmode, 0, Qnil, &oflags, &fmode, &convconfig);
1845
1953
  enc = convconfig.enc2;
1846
1954
  }
1847
1955
  }
1848
1956
  ptr->enc = enc;
1849
- if (!NIL_P(ptr->string) && WRITABLE(self)) {
1957
+ if (!NIL_P(ptr->string) && WRITABLE(self) && !str_chilled_p(ptr->string)) {
1850
1958
  rb_enc_associate(ptr->string, enc);
1851
1959
  }
1852
1960
 
@@ -1872,16 +1980,9 @@ strio_set_encoding_by_bom(VALUE self)
1872
1980
  }
1873
1981
 
1874
1982
  /*
1875
- * \IO streams for strings, with access similar to
1876
- * {IO}[rdoc-ref:IO];
1877
- * see {IO}[rdoc-ref:IO].
1878
- *
1879
- * === About the Examples
1880
- *
1881
- * Examples on this page assume that \StringIO has been required:
1882
- *
1883
- * require 'stringio'
1983
+ * :markup: markdown
1884
1984
  *
1985
+ * :include: stringio/stringio.md
1885
1986
  */
1886
1987
  void
1887
1988
  Init_stringio(void)
@@ -1889,7 +1990,7 @@ Init_stringio(void)
1889
1990
  #undef rb_intern
1890
1991
 
1891
1992
  #ifdef HAVE_RB_EXT_RACTOR_SAFE
1892
- rb_ext_ractor_safe(true);
1993
+ rb_ext_ractor_safe(true);
1893
1994
  #endif
1894
1995
 
1895
1996
  VALUE StringIO = rb_define_class("StringIO", rb_cObject);
@@ -1981,7 +2082,9 @@ Init_stringio(void)
1981
2082
  rb_define_method(StringIO, "set_encoding_by_bom", strio_set_encoding_by_bom, 0);
1982
2083
 
1983
2084
  {
2085
+ /* :stopdoc: */
1984
2086
  VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
2087
+ /* :startdoc: */
1985
2088
  rb_define_method(mReadable, "readchar", strio_readchar, 0);
1986
2089
  rb_define_method(mReadable, "readbyte", strio_readbyte, 0);
1987
2090
  rb_define_method(mReadable, "readline", strio_readline, -1);
@@ -1991,7 +2094,9 @@ Init_stringio(void)
1991
2094
  rb_include_module(StringIO, mReadable);
1992
2095
  }
1993
2096
  {
2097
+ /* :stopdoc: */
1994
2098
  VALUE mWritable = rb_define_module_under(rb_cIO, "generic_writable");
2099
+ /* :startdoc: */
1995
2100
  rb_define_method(mWritable, "<<", strio_addstr, 1);
1996
2101
  rb_define_method(mWritable, "print", strio_print, -1);
1997
2102
  rb_define_method(mWritable, "printf", strio_printf, -1);
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stringio
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nobu Nakada
8
8
  - Charles Oliver Nutter
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2024-06-13 00:00:00.000000000 Z
11
+ date: 2025-12-17 00:00:00.000000000 Z
13
12
  dependencies: []
14
13
  description: Pseudo `IO` class from/to `String`.
15
14
  email:
@@ -42,8 +41,8 @@ homepage: https://github.com/ruby/stringio
42
41
  licenses:
43
42
  - Ruby
44
43
  - BSD-2-Clause
45
- metadata: {}
46
- post_install_message:
44
+ metadata:
45
+ changelog_uri: https://github.com/ruby/stringio/releases/tag/v3.2.0
47
46
  rdoc_options: []
48
47
  require_paths:
49
48
  - lib
@@ -58,8 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
57
  - !ruby/object:Gem::Version
59
58
  version: '0'
60
59
  requirements: []
61
- rubygems_version: 3.5.11
62
- signing_key:
60
+ rubygems_version: 3.6.9
63
61
  specification_version: 4
64
62
  summary: Pseudo IO on String
65
63
  test_files: []