1. my class IO::Path { ... }
  2. my class IO::Special { ... }
  3. my class Proc { ... }
  4. my class IO::Handle {
  5. has $.path;
  6. has $!PIO;
  7. has $.chomp is rw = Bool::True;
  8. has $.nl-in = ["\x0A", "\r\n"];
  9. has Str:D $.nl-out is rw = "\n";
  10. has Str $.encoding;
  11. has Rakudo::Internals::VMBackedDecoder $!decoder;
  12. submethod TWEAK (:$encoding, :$bin) {
  13. nqp::if(
  14. $bin,
  15. nqp::isconcrete($encoding) && X::IO::BinaryAndEncoding.new.throw,
  16. $!encoding = $encoding || 'utf8')
  17. }
  18. method open(IO::Handle:D:
  19. :$r, :$w, :$x, :$a, :$update,
  20. :$rw, :$rx, :$ra,
  21. :$mode is copy,
  22. :$create is copy,
  23. :$append is copy,
  24. :$truncate is copy,
  25. :$exclusive is copy,
  26. :$bin,
  27. :$enc is copy,
  28. :$chomp = $!chomp,
  29. :$nl-in is copy = $!nl-in,
  30. Str:D :$nl-out is copy = $!nl-out,
  31. ) {
  32. nqp::if(
  33. $bin,
  34. nqp::stmts(
  35. nqp::isconcrete($enc) && X::IO::BinaryAndEncoding.new.throw,
  36. $!encoding = Nil),
  37. nqp::unless(
  38. nqp::isconcrete($enc),
  39. $enc = $!encoding));
  40. $mode = nqp::if(
  41. $mode,
  42. nqp::if(nqp::istype($mode, Str), $mode, $mode.Str),
  43. nqp::if(
  44. nqp::unless(nqp::if($r, $w), $rw), # $r && $w || $rw
  45. nqp::stmts(($create = True), 'rw'),
  46. nqp::if(
  47. nqp::unless(nqp::if($r, $x), $rx),
  48. nqp::stmts(($create = $exclusive = True), 'rw'),
  49. nqp::if(
  50. nqp::unless(nqp::if($r, $a), $ra),
  51. nqp::stmts(($create = $append = True), 'rw'),
  52. nqp::if(
  53. $r, 'ro',
  54. nqp::if(
  55. $w,
  56. nqp::stmts(($create = $truncate = True), 'wo'),
  57. nqp::if(
  58. $x,
  59. nqp::stmts(($create = $exclusive = True), 'wo'),
  60. nqp::if(
  61. $a,
  62. nqp::stmts(($create = $append = True), 'wo'),
  63. nqp::if(
  64. $update, 'rw',
  65. 'ro'
  66. ),
  67. ),
  68. ),
  69. ),
  70. ),
  71. ),
  72. ),
  73. ),
  74. );
  75. nqp::if(
  76. nqp::iseq_s($!path.Str, '-'),
  77. nqp::stmts(
  78. nqp::if(
  79. nqp::iseq_s($mode, 'ro'),
  80. (return $*IN),
  81. nqp::if(
  82. nqp::iseq_s($mode, 'wo'),
  83. (return $*OUT),
  84. die("Cannot open standard stream in mode '$mode'"),
  85. ),
  86. ),
  87. ),
  88. );
  89. if nqp::istype($!path, IO::Special) {
  90. my $what := $!path.what;
  91. if $what eq '<STDIN>' {
  92. $!PIO := nqp::getstdin();
  93. }
  94. elsif $what eq '<STDOUT>' {
  95. $!PIO := nqp::getstdout();
  96. }
  97. elsif $what eq '<STDERR>' {
  98. $!PIO := nqp::getstderr();
  99. }
  100. else {
  101. die "Don't know how to open '$_' especially";
  102. }
  103. $!chomp = $chomp;
  104. $!nl-out = $nl-out;
  105. if nqp::isconcrete($enc) {
  106. $!encoding = Rakudo::Internals.NORMALIZE_ENCODING($enc);
  107. $!decoder := Rakudo::Internals::VMBackedDecoder.new($!encoding, :translate-nl);
  108. $!decoder.set-line-separators(($!nl-in = $nl-in).list);
  109. }
  110. return self;
  111. }
  112. fail X::IO::Directory.new(:$!path, :trying<open>) if $!path.d;
  113. {
  114. CATCH { .fail }
  115. $!PIO := nqp::open(
  116. $!path.absolute,
  117. nqp::concat(
  118. nqp::if(
  119. nqp::iseq_s($mode, 'ro'), 'r',
  120. nqp::if(
  121. nqp::iseq_s($mode, 'wo'), '-',
  122. nqp::if(
  123. nqp::iseq_s($mode, 'rw'), '+',
  124. die("Unknown mode '$mode'")
  125. ),
  126. ),
  127. ),
  128. nqp::concat(
  129. nqp::if($create, 'c', ''),
  130. nqp::concat(
  131. nqp::if($append, 'a', ''),
  132. nqp::concat(
  133. nqp::if($truncate, 't', ''),
  134. nqp::if($exclusive, 'x', ''),
  135. ),
  136. ),
  137. )
  138. ),
  139. );
  140. }
  141. $!chomp = $chomp;
  142. $!nl-out = $nl-out;
  143. if nqp::isconcrete($enc) {
  144. $!encoding = Rakudo::Internals.NORMALIZE_ENCODING($enc);
  145. $!decoder := Rakudo::Internals::VMBackedDecoder.new($!encoding, :translate-nl);
  146. $!decoder.set-line-separators(($!nl-in = $nl-in).list);
  147. }
  148. self;
  149. }
  150. method nl-in is rw {
  151. Proxy.new(
  152. FETCH => {
  153. $!nl-in
  154. },
  155. STORE => -> $, $nl-in {
  156. $!nl-in = $nl-in;
  157. $!decoder && $!decoder.set-line-separators($nl-in.list);
  158. $nl-in
  159. }
  160. );
  161. }
  162. method close(IO::Handle:D: --> True) {
  163. nqp::if(
  164. nqp::defined($!PIO),
  165. nqp::stmts(
  166. nqp::closefh($!PIO), # TODO: catch errors
  167. $!PIO := nqp::null
  168. )
  169. )
  170. }
  171. method eof(IO::Handle:D:) {
  172. nqp::p6bool($!decoder
  173. ?? $!decoder.is-empty && nqp::eoffh($!PIO)
  174. !! nqp::eoffh($!PIO));
  175. }
  176. method get(IO::Handle:D:) {
  177. $!decoder or die X::IO::BinaryMode.new(:trying<get>);
  178. $!decoder.consume-line-chars(:$!chomp) // self!get-line-slow-path()
  179. }
  180. method !get-line-slow-path() {
  181. my $line := Nil;
  182. loop {
  183. my $buf := nqp::readfh($!PIO, buf8.new, 0x100000);
  184. if $buf.elems {
  185. $!decoder.add-bytes($buf);
  186. $line := $!decoder.consume-line-chars(:$!chomp);
  187. last if nqp::isconcrete($line);
  188. }
  189. else {
  190. $line := $!decoder.consume-line-chars(:$!chomp, :eof)
  191. unless nqp::eoffh($!PIO) && $!decoder.is-empty;
  192. last;
  193. }
  194. }
  195. $line
  196. }
  197. method getc(IO::Handle:D:) {
  198. $!decoder or die X::IO::BinaryMode.new(:trying<getc>);
  199. $!decoder.consume-exactly-chars(1) || self!getc-slow-path()
  200. }
  201. method !getc-slow-path() {
  202. $!decoder.add-bytes(nqp::readfh($!PIO, buf8.new, 0x100000));
  203. $!decoder.consume-exactly-chars(1) // $!decoder.consume-all-chars() || Nil
  204. }
  205. # XXX TODO: Make these routine read handle lazily when we have Cat type
  206. method comb (IO::Handle:D: :$close, |c) {
  207. $!decoder or die X::IO::BinaryMode.new(:trying<comb>);
  208. self.slurp(:$close).comb: |c
  209. }
  210. method split(IO::Handle:D: :$close, |c) {
  211. $!decoder or die X::IO::BinaryMode.new(:trying<split>);
  212. self.slurp(:$close).split: |c
  213. }
  214. proto method words (|) { * }
  215. multi method words(IO::Handle:D \SELF: $limit, :$close) {
  216. $!decoder or die X::IO::BinaryMode.new(:trying<words>);
  217. nqp::istype($limit,Whatever) || $limit == Inf
  218. ?? self.words(:$close)
  219. !! $close
  220. ?? Seq.new(Rakudo::Iterator.FirstNThenSinkAll(
  221. self.words.iterator, $limit.Int, {SELF.close}))
  222. !! self.words.head($limit.Int)
  223. }
  224. multi method words(IO::Handle:D: :$close) {
  225. $!decoder or die X::IO::BinaryMode.new(:trying<words>);
  226. Seq.new(class :: does Iterator {
  227. has $!handle;
  228. has $!close;
  229. has str $!str;
  230. has int $!pos;
  231. has int $!searching;
  232. method !SET-SELF(\handle, $!close) {
  233. $!handle := handle;
  234. $!searching = 1;
  235. $!str = ""; # RT #126492
  236. self!next-chunk;
  237. self
  238. }
  239. method new(\handle, \close) {
  240. nqp::create(self)!SET-SELF(handle, close);
  241. }
  242. method !next-chunk() {
  243. my int $chars = nqp::chars($!str);
  244. $!str = $!pos < $chars ?? nqp::substr($!str,$!pos) !! "";
  245. $chars = nqp::chars($!str);
  246. while $!searching {
  247. $!str = nqp::concat($!str,$!handle.readchars);
  248. my int $new = nqp::chars($!str);
  249. $!searching = 0 if $new == $chars; # end
  250. $!pos = ($chars = $new)
  251. ?? nqp::findnotcclass(
  252. nqp::const::CCLASS_WHITESPACE, $!str, 0, $chars)
  253. !! 0;
  254. last if $!pos < $chars;
  255. }
  256. }
  257. method pull-one() {
  258. my int $chars;
  259. my int $left;
  260. my int $nextpos;
  261. while ($chars = nqp::chars($!str)) && $!searching {
  262. while ($left = $chars - $!pos) > 0 {
  263. $nextpos = nqp::findcclass(
  264. nqp::const::CCLASS_WHITESPACE,$!str,$!pos,$left);
  265. last unless $left = $chars - $nextpos; # broken word
  266. my str $found =
  267. nqp::substr($!str, $!pos, $nextpos - $!pos);
  268. $!pos = nqp::findnotcclass(
  269. nqp::const::CCLASS_WHITESPACE,$!str,$nextpos,$left);
  270. return nqp::p6box_s($found);
  271. }
  272. self!next-chunk;
  273. }
  274. if $!pos < $chars {
  275. my str $found = nqp::substr($!str,$!pos);
  276. $!pos = $chars;
  277. nqp::p6box_s($found)
  278. }
  279. else {
  280. $!handle.close if $!close;
  281. IterationEnd
  282. }
  283. }
  284. method push-all($target --> IterationEnd) {
  285. my int $chars;
  286. my int $left;
  287. my int $nextpos;
  288. while ($chars = nqp::chars($!str)) && $!searching {
  289. while ($left = $chars - $!pos) > 0 {
  290. $nextpos = nqp::findcclass(
  291. nqp::const::CCLASS_WHITESPACE,$!str,$!pos,$left);
  292. last unless $left = $chars - $nextpos; # broken word
  293. $target.push(nqp::p6box_s(
  294. nqp::substr($!str, $!pos, $nextpos - $!pos)
  295. ));
  296. $!pos = nqp::findnotcclass(
  297. nqp::const::CCLASS_WHITESPACE,$!str,$nextpos,$left);
  298. }
  299. self!next-chunk;
  300. }
  301. $target.push(nqp::p6box_s(nqp::substr($!str,$!pos)))
  302. if $!pos < $chars;
  303. $!handle.close if $close;
  304. }
  305. }.new(self, $close));
  306. }
  307. my role PIOIterator does Iterator {
  308. has $!handle;
  309. has $!chomp;
  310. has $!decoder;
  311. method new(\handle) {
  312. my \res = nqp::create(self);
  313. nqp::bindattr(res, self.WHAT, '$!handle', handle);
  314. nqp::bindattr(res, self.WHAT, '$!chomp',
  315. nqp::getattr(handle, IO::Handle, '$!chomp'));
  316. nqp::p6bindattrinvres(res, self.WHAT, '$!decoder',
  317. nqp::getattr(handle, IO::Handle, '$!decoder'))
  318. }
  319. method sink-all(--> IterationEnd) {
  320. nqp::seekfh(nqp::getattr($!handle, IO::Handle, '$!PIO'), 0, 2) # seek to end
  321. }
  322. }
  323. method !LINES-ITERATOR (IO::Handle:D:) {
  324. $!decoder or die X::IO::BinaryMode.new(:trying<lines>);
  325. (nqp::eqaddr(self.WHAT,IO::Handle)
  326. ?? (class :: does PIOIterator { # exact type, can shortcircuit get
  327. method pull-one() {
  328. # Slow path falls back to .get on the handle, which will
  329. # replenish the buffer once we exhaust it.
  330. $!decoder.consume-line-chars(:$!chomp) // $!handle.get // IterationEnd
  331. }
  332. method push-all($target --> IterationEnd) {
  333. nqp::while(
  334. nqp::isconcrete(my $line :=
  335. $!decoder.consume-line-chars(:$!chomp) // $!handle.get),
  336. $target.push($line)
  337. )
  338. }
  339. })
  340. !! (class :: does Iterator { # can *NOT* shortcircuit .get
  341. has $!handle;
  342. method new(\handle) {
  343. nqp::p6bindattrinvres(
  344. nqp::create(self),self.WHAT,'$!handle',handle)
  345. }
  346. method pull-one() {
  347. nqp::if(
  348. (my $line := $!handle.get).DEFINITE,
  349. $line,
  350. IterationEnd
  351. )
  352. }
  353. method push-all($target --> IterationEnd) {
  354. nqp::while(
  355. (my $line := $!handle.get).DEFINITE,
  356. $target.push($line)
  357. )
  358. }
  359. method sink-all(--> IterationEnd) {
  360. # can't seek pipes, so need the `try`
  361. try $!handle.seek(0,SeekFromEnd) # seek to end
  362. }
  363. })
  364. ).new(self)
  365. }
  366. proto method lines (|) { * }
  367. multi method lines(IO::Handle:D \SELF: $limit, :$close) {
  368. nqp::istype($limit,Whatever) || $limit == Inf
  369. ?? self.lines(:$close)
  370. !! $close
  371. ?? Seq.new(Rakudo::Iterator.FirstNThenSinkAll(
  372. self!LINES-ITERATOR, $limit.Int, {SELF.close}))
  373. !! self.lines.head($limit.Int)
  374. }
  375. multi method lines(IO::Handle:D \SELF: :$close!) {
  376. Seq.new(
  377. $close # use -1 as N in FirstNThenSinkAllSeq to get all items
  378. ?? Rakudo::Iterator.FirstNThenSinkAll(
  379. self!LINES-ITERATOR, -1, {SELF.close})
  380. !! self!LINES-ITERATOR
  381. )
  382. }
  383. multi method lines(IO::Handle:D:) { Seq.new(self!LINES-ITERATOR) }
  384. method read(IO::Handle:D: Int(Cool:D) $bytes) {
  385. # If we have one, read bytes via. the decoder to support mixed-mode I/O.
  386. $!decoder
  387. ?? ($!decoder.consume-exactly-bytes($bytes) // self!read-slow-path($bytes))
  388. !! nqp::readfh($!PIO,buf8.new,nqp::unbox_i($bytes))
  389. }
  390. method !read-slow-path($bytes) {
  391. if nqp::eoffh($!PIO) && $!decoder.is-empty {
  392. buf8.new
  393. }
  394. else {
  395. $!decoder.add-bytes(nqp::readfh($!PIO, buf8.new, $bytes max 0x10000));
  396. $!decoder.consume-exactly-bytes($bytes)
  397. // $!decoder.consume-exactly-bytes($!decoder.bytes-available)
  398. // buf8.new
  399. }
  400. }
  401. method readchars(Int(Cool:D) $chars = $*DEFAULT-READ-ELEMS) {
  402. $!decoder or die X::IO::BinaryMode.new(:trying<readchars>);
  403. $!decoder.consume-exactly-chars($chars) // self!readchars-slow-path($chars)
  404. }
  405. method !readchars-slow-path($chars) {
  406. my $result := '';
  407. unless nqp::eoffh($!PIO) && $!decoder.is-empty {
  408. loop {
  409. my $buf := nqp::readfh($!PIO, buf8.new, 0x100000);
  410. if $buf.elems {
  411. $!decoder.add-bytes($buf);
  412. $result := $!decoder.consume-exactly-chars($chars);
  413. last if nqp::isconcrete($result);
  414. }
  415. else {
  416. $result := $!decoder.consume-all-chars();
  417. last;
  418. }
  419. }
  420. }
  421. $result
  422. }
  423. method Supply(IO::Handle:D: :$size = $*DEFAULT-READ-ELEMS --> Supply:D) {
  424. if $!decoder { # handle is in character mode
  425. supply {
  426. my int $chars = $size;
  427. my str $str = self.readchars($chars);
  428. nqp::while(
  429. nqp::chars($str),
  430. nqp::stmts(
  431. (emit nqp::p6box_s($str)),
  432. ($str = self.readchars($chars))
  433. )
  434. );
  435. done;
  436. }
  437. }
  438. else {
  439. supply {
  440. my $buf := self.read($size);
  441. nqp::while(
  442. nqp::elems($buf),
  443. nqp::stmts(
  444. (emit $buf),
  445. ($buf := self.read($size))
  446. )
  447. );
  448. done;
  449. }
  450. }
  451. }
  452. proto method seek(|) { * }
  453. multi method seek(IO::Handle:D: Int:D $offset, SeekType:D $whence = SeekFromBeginning) {
  454. my int $rewind = 0;
  455. if $!decoder {
  456. # consider bytes we pre-read, when seeking from current position:
  457. $rewind = $!decoder.bytes-available if
  458. nqp::eqaddr(nqp::decont($whence), SeekFromCurrent);
  459. # Freshen decoder, so we won't have stuff left over from earlier reads
  460. # that were in the wrong place.
  461. $!decoder := Rakudo::Internals::VMBackedDecoder.new($!encoding, :translate-nl);
  462. $!decoder.set-line-separators($!nl-in.list);
  463. }
  464. nqp::seekfh($!PIO, $offset - $rewind, +$whence);
  465. }
  466. method tell(IO::Handle:D: --> Int:D) {
  467. nqp::tellfh($!PIO) - ($!decoder ?? $!decoder.bytes-available !! 0)
  468. }
  469. method write(IO::Handle:D: Blob:D $buf --> True) {
  470. nqp::writefh($!PIO, nqp::decont($buf));
  471. }
  472. method opened(IO::Handle:D:) {
  473. nqp::p6bool(nqp::istrue($!PIO));
  474. }
  475. method t(IO::Handle:D:) {
  476. self.opened && nqp::p6bool(nqp::isttyfh($!PIO))
  477. }
  478. method lock(IO::Handle:D:
  479. Bool:D :$non-blocking = False, Bool:D :$shared = False --> True
  480. ) {
  481. nqp::lockfh($!PIO, 0x10*$non-blocking + $shared);
  482. CATCH { default {
  483. fail X::IO::Lock.new: :os-error(.Str),
  484. :lock-type( 'non-' x $non-blocking ~ 'blocking, '
  485. ~ ($shared ?? 'shared' !! 'exclusive') );
  486. }}
  487. }
  488. method unlock(IO::Handle:D: --> True) {
  489. nqp::unlockfh($!PIO);
  490. }
  491. method printf(IO::Handle:D: |c) {
  492. self.print(sprintf |c);
  493. }
  494. proto method print(|) { * }
  495. multi method print(IO::Handle:D: Str:D \x --> True) {
  496. $!decoder or die X::IO::BinaryMode.new(:trying<print>);
  497. nqp::writefh($!PIO, x.encode($!encoding, :translate-nl));
  498. }
  499. multi method print(IO::Handle:D: **@list is raw --> True) { # is raw gives List, which is cheaper
  500. self.print(@list.join);
  501. }
  502. proto method put(|) { * }
  503. multi method put(IO::Handle:D: Str:D \x --> True) {
  504. $!decoder or die X::IO::BinaryMode.new(:trying<put>);
  505. nqp::writefh($!PIO,
  506. nqp::concat(nqp::unbox_s(x), nqp::unbox_s($!nl-out)).encode($!encoding, :translate-nl))
  507. }
  508. multi method put(IO::Handle:D: **@list is raw --> True) { # is raw gives List, which is cheaper
  509. self.put(@list.join);
  510. }
  511. multi method say(IO::Handle:D: \x --> True) {
  512. $!decoder or die X::IO::BinaryMode.new(:trying<say>);
  513. nqp::writefh($!PIO,
  514. nqp::concat(nqp::unbox_s(x.gist), nqp::unbox_s($!nl-out)).encode($!encoding, :translate-nl))
  515. }
  516. multi method say(IO::Handle:D: |) {
  517. $!decoder or die X::IO::BinaryMode.new(:trying<say>);
  518. my Mu $args := nqp::p6argvmarray();
  519. nqp::shift($args);
  520. my str $conc = '';
  521. $conc = nqp::concat($conc, nqp::shift($args).gist) while $args;
  522. self.print(nqp::concat($conc, $!nl-out));
  523. }
  524. method print-nl(IO::Handle:D: --> True) {
  525. $!decoder or die X::IO::BinaryMode.new(:trying<print-nl>);
  526. nqp::writefh($!PIO, $!nl-out.encode($!encoding, :translate-nl));
  527. }
  528. proto method slurp-rest(|) { * }
  529. multi method slurp-rest(IO::Handle:D: :$bin! where *.so, :$close --> Buf:D) {
  530. # NOTE: THIS METHOD WILL BE DEPRECATED IN 6.d in favour of .slurp()
  531. # Testing of it in roast master has been removed and only kept in 6.c
  532. # If you're changing this code for whatever reason, test with 6.c-errata
  533. LEAVE self.close if $close;
  534. my $res := buf8.new;
  535. loop {
  536. my $buf := self.read(0x100000);
  537. nqp::elems($buf)
  538. ?? $res.append($buf)
  539. !! return $res
  540. }
  541. }
  542. multi method slurp-rest(IO::Handle:D: :$enc, :$bin, :$close --> Str:D) {
  543. # NOTE: THIS METHOD WILL BE DEPRECATED IN 6.d in favour of .slurp()
  544. # Testing of it in roast master has been removed and only kept in 6.c
  545. # If you're changing this code for whatever reason, test with 6.c-errata
  546. $!decoder or die X::IO::BinaryMode.new(:trying<slurp-rest>);
  547. LEAVE self.close if $close;
  548. self.encoding($enc) if $enc.defined;
  549. self!slurp-all-chars()
  550. }
  551. method slurp(IO::Handle:D: :$close) {
  552. my $res;
  553. nqp::if(
  554. $!decoder,
  555. ($res := self!slurp-all-chars()),
  556. nqp::stmts(
  557. ($res := buf8.new),
  558. nqp::while(
  559. nqp::elems(my $buf := nqp::readfh($!PIO, buf8.new, 0x100000)),
  560. $res.append($buf)
  561. )
  562. )
  563. );
  564. # don't sink result of .close; it might be a failed Proc
  565. $ = self.close if $close;
  566. $res
  567. }
  568. method !slurp-all-chars() {
  569. while nqp::elems(my $buf := nqp::readfh($!PIO, buf8.new, 0x100000)) {
  570. $!decoder.add-bytes($buf);
  571. }
  572. $!decoder.consume-all-chars()
  573. }
  574. proto method spurt(|) { * }
  575. multi method spurt(IO::Handle:D: Blob $data, :$close) {
  576. LEAVE self.close if $close;
  577. self.write($data);
  578. }
  579. multi method spurt(IO::Handle:D: Cool $data, :$close) {
  580. LEAVE self.close if $close;
  581. self.print($data);
  582. }
  583. method path(IO::Handle:D:) { $!path.IO }
  584. method IO(IO::Handle:D:) { $!path.IO }
  585. # use $.path, so IO::Pipe picks it up
  586. multi method Str(IO::Handle:D:) { $.path.Str }
  587. multi method gist(IO::Handle:D:) {
  588. "{self.^name}<$!path.gist()>({self.opened ?? 'opened' !! 'closed'})"
  589. }
  590. method flush(IO::Handle:D: --> True) {
  591. CATCH { default { fail X::IO::Flush.new: :os-error(.Str) } }
  592. nqp::defined($!PIO) or die 'File handle not open, so cannot flush';
  593. nqp::flushfh($!PIO);
  594. }
  595. proto method encoding(|) { * }
  596. multi method encoding(IO::Handle:D:) { $!encoding // Nil }
  597. multi method encoding(IO::Handle:D: $new-encoding is copy) {
  598. with $new-encoding {
  599. if $_ eq 'bin' {
  600. $_ = Nil;
  601. }
  602. else {
  603. $_ = Rakudo::Internals.NORMALIZE_ENCODING(.Str);
  604. return $!encoding if $!encoding && $!encoding eq $_;
  605. }
  606. }
  607. with $!decoder {
  608. # We're switching encoding, or back to binary mode. First grab any
  609. # bytes the current decoder is holding on to but has not yet done
  610. # decoding of.
  611. my $available = $!decoder.bytes-available;
  612. with $new-encoding {
  613. my $prev-decoder := $!decoder;
  614. $!decoder := Rakudo::Internals::VMBackedDecoder.new($new-encoding, :translate-nl);
  615. $!decoder.set-line-separators($!nl-in.list);
  616. $!decoder.add-bytes($prev-decoder.consume-exactly-bytes($available))
  617. if $available;
  618. $!encoding = $new-encoding;
  619. }
  620. else {
  621. nqp::seekfh($!PIO, -$available, SeekFromCurrent) if $available;
  622. $!decoder := Rakudo::Internals::VMBackedDecoder;
  623. $!encoding = Nil;
  624. Nil
  625. }
  626. }
  627. else {
  628. # No previous decoder; make a new one if needed, otherwise no change.
  629. with $new-encoding {
  630. $!decoder := Rakudo::Internals::VMBackedDecoder.new($new-encoding, :translate-nl);
  631. $!decoder.set-line-separators($!nl-in.list);
  632. $!encoding = $new-encoding;
  633. }
  634. else {
  635. Nil
  636. }
  637. }
  638. }
  639. submethod DESTROY(IO::Handle:D:) {
  640. nqp::if(
  641. nqp::defined($!PIO),
  642. nqp::stmts(
  643. nqp::closefh($!PIO), # don't bother checking for errors
  644. $!PIO := nqp::null
  645. )
  646. )
  647. }
  648. method native-descriptor(IO::Handle:D:) {
  649. nqp::filenofh($!PIO)
  650. }
  651. }
  652. Rakudo::Internals.REGISTER-DYNAMIC: '$*DEFAULT-READ-ELEMS', {
  653. PROCESS::<$DEFAULT-READ-ELEMS> := %*ENV<RAKUDO_DEFAULT_READ_ELEMS> // 65536;
  654. }