1. my class X::Buf::AsStr { ... }
  2. my class X::Buf::Pack { ... }
  3. my class X::Buf::Pack::NonASCII { ... }
  4. my class X::Cannot::Empty { ... }
  5. my class X::Cannot::Lazy { ... }
  6. my class X::Experimental { ... }
  7. my role Blob[::T = uint8] does Positional[T] does Stringy is repr('VMArray') is array_type(T) {
  8. X::NYI.new(
  9. feature => "{$?CLASS.^name.comb(/^ \w+ /)}s with native {T.^name}"
  10. ).throw unless nqp::istype(T,Int);
  11. # other then *8 not supported yet
  12. my int $bpe = try {
  13. (T.^nativesize / 8).Int
  14. } // 1;
  15. multi method WHICH(Blob:D:) {
  16. nqp::box_s(
  17. nqp::concat(
  18. nqp::if(
  19. nqp::eqaddr(self.WHAT,Blob),
  20. 'Blob|',
  21. nqp::concat(nqp::unbox_s(self.^name), '|')
  22. ),
  23. nqp::sha1(self.decode("latin-1"))
  24. ),
  25. ValueObjAt
  26. )
  27. }
  28. multi method new(Blob:) { nqp::create(self) }
  29. multi method new(Blob: Blob:D $blob) {
  30. nqp::splice(nqp::create(self),$blob,0,0)
  31. }
  32. multi method new(Blob: int @values) {
  33. nqp::splice(nqp::create(self),@values,0,0)
  34. }
  35. multi method new(Blob: @values) {
  36. @values.is-lazy
  37. ?? Failure.new(X::Cannot::Lazy.new(:action<new>,:what(self.^name)))
  38. !! self!push-list("initializ",nqp::create(self),@values)
  39. }
  40. multi method new(Blob: *@values) { self.new(@values) }
  41. proto method allocate(|) {*}
  42. multi method allocate(Blob:U: Int:D $elements) {
  43. nqp::setelems(nqp::create(self),$elements)
  44. }
  45. multi method allocate(Blob:U: Int:D $elements, int $value) {
  46. my int $elems = $elements;
  47. my $blob := nqp::setelems(nqp::create(self),$elems);
  48. my int $i = -1;
  49. nqp::bindpos_i($blob,$i,$value) while nqp::islt_i(++$i,$elems);
  50. $blob;
  51. }
  52. multi method allocate(Blob:U: Int:D $elements, Int:D \value) {
  53. my int $value = value;
  54. self.allocate($elements,$value)
  55. }
  56. multi method allocate(Blob:U: Int:D $elements, Mu:D $got) {
  57. self!fail-typecheck('allocate',$got)
  58. }
  59. multi method allocate(Blob:U: Int:D $elements, int @values) {
  60. self!spread(nqp::setelems(nqp::create(self),$elements),@values)
  61. }
  62. multi method allocate(Blob:U: Int:D $elements, Blob:D $blob) {
  63. self!spread(nqp::setelems(nqp::create(self),$elements),$blob)
  64. }
  65. multi method allocate(Blob:U: Int:D $elements, @values) {
  66. self!spread(nqp::setelems(nqp::create(self),$elements),Blob.new(@values))
  67. }
  68. multi method EXISTS-POS(Blob:D: int \pos) {
  69. nqp::p6bool(
  70. nqp::islt_i(pos,nqp::elems(self)) && nqp::isge_i(pos,0)
  71. );
  72. }
  73. multi method EXISTS-POS(Blob:D: Int:D \pos) {
  74. nqp::p6bool(
  75. nqp::islt_i(pos,nqp::elems(self)) && nqp::isge_i(pos,0)
  76. );
  77. }
  78. multi method AT-POS(Blob:D: int \pos) {
  79. nqp::if(
  80. (nqp::isge_i(pos,nqp::elems(self)) || nqp::islt_i(pos,0)),
  81. self!fail-range(pos),
  82. nqp::atpos_i(self,pos)
  83. )
  84. }
  85. multi method AT-POS(Blob:D: Int:D \pos) {
  86. nqp::if(
  87. (nqp::isge_i(pos,nqp::elems(self)) || nqp::islt_i(pos,0)),
  88. self!fail-range(pos),
  89. nqp::atpos_i(self,pos)
  90. )
  91. }
  92. multi method Bool(Blob:D:) { nqp::p6bool(nqp::elems(self)) }
  93. method Capture(Blob:D:) { self.List.Capture }
  94. multi method elems(Blob:D:) { nqp::p6box_i(nqp::elems(self)) }
  95. multi method elems(Blob:U: --> 1) { }
  96. method Numeric(Blob:D:) { nqp::p6box_i(nqp::elems(self)) }
  97. method Int(Blob:D:) { nqp::p6box_i(nqp::elems(self)) }
  98. method bytes(Blob:D:) { nqp::mul_i(nqp::elems(self),$bpe) }
  99. method chars(Blob:D:) { X::Buf::AsStr.new(method => 'chars').throw }
  100. multi method Str(Blob:D:) { X::Buf::AsStr.new(method => 'Str' ).throw }
  101. multi method Stringy(Blob:D:) { X::Buf::AsStr.new(method => 'Stringy' ).throw }
  102. proto method decode(|) {*}
  103. multi method decode(Blob:D:) {
  104. nqp::p6box_s(nqp::decode(self, 'utf8'))
  105. }
  106. multi method decode(Blob:D: $encoding, Str :$replacement!, Bool:D :$strict = False) {
  107. nqp::p6box_s(
  108. nqp::decoderepconf(self,
  109. Rakudo::Internals.NORMALIZE_ENCODING($encoding),
  110. $replacement.defined ?? $replacement !! nqp::null_s(),
  111. $strict ?? 0 !! 1))
  112. }
  113. multi method decode(Blob:D: $encoding, Bool:D :$strict = False) {
  114. nqp::p6box_s(
  115. nqp::decodeconf(self,
  116. Rakudo::Internals.NORMALIZE_ENCODING($encoding),
  117. $strict ?? 0 !! 1))
  118. }
  119. multi method list(Blob:D:) {
  120. Seq.new(class :: does Rakudo::Iterator::Blobby {
  121. method pull-one() is raw {
  122. nqp::if(
  123. nqp::islt_i(($!i = nqp::add_i($!i,1)),nqp::elems($!blob)),
  124. nqp::atpos_i($!blob,$!i),
  125. IterationEnd
  126. )
  127. }
  128. }.new(self)).cache
  129. }
  130. multi method gist(Blob:D:) {
  131. self.^name ~ ':0x<' ~ self.map( -> \el {
  132. state $i = 0;
  133. ++$i == 101 ?? '...'
  134. !! $i == 102 ?? last()
  135. !! nqp::if(nqp::iseq_i( # el.fmt: '%02x'
  136. nqp::chars(my str $v = nqp::lc(el.base: 16)),1),
  137. nqp::concat('0',$v),$v)
  138. }) ~ '>'
  139. }
  140. multi method perl(Blob:D:) {
  141. self.^name ~ '.new(' ~ self.join(',') ~ ')';
  142. }
  143. method subbuf(Blob:D: $from, $length?) {
  144. nqp::stmts(
  145. (my int $elems = nqp::elems(self)),
  146. nqp::if(
  147. $length.DEFINITE && $length < 0,
  148. X::OutOfRange.new(
  149. :what('Len element to subbuf'), :got($length), :range("0..$elems"),
  150. ).fail,
  151. nqp::stmts(
  152. (my int $pos),
  153. (my int $todo),
  154. nqp::if(
  155. nqp::istype($from,Range),
  156. nqp::stmts(
  157. $from.int-bounds($pos, my int $max),
  158. ($todo = nqp::add_i(nqp::sub_i($max, $pos), 1))),
  159. nqp::stmts(
  160. ($pos = nqp::istype($from, Callable) ?? $from($elems) !! $from.Int),
  161. ($todo = $length.DEFINITE ?? $length.Int min $elems - $pos !! $elems - $pos))),
  162. nqp::if(
  163. nqp::islt_i($pos, 0),
  164. X::OutOfRange.new(
  165. :what('From argument to subbuf'), :got($from.gist), :range("0..$elems"),
  166. :comment("use *-{abs $pos} if you want to index relative to the end"),
  167. ).fail,
  168. nqp::if(
  169. nqp::isgt_i($pos, $elems),
  170. X::OutOfRange.new(
  171. :what('From argument to subbuf'), :got($from.gist), :range("0..$elems"),
  172. ).fail,
  173. nqp::if(
  174. nqp::isle_i($todo, 0),
  175. nqp::create(self), # we want zero elements; return empty Blob
  176. nqp::stmts(
  177. (my $subbuf := nqp::create(self)),
  178. nqp::setelems($subbuf, $todo),
  179. (my int $i = -1),
  180. --$pos,
  181. nqp::while(
  182. nqp::islt_i(++$i,$todo),
  183. nqp::bindpos_i($subbuf, $i, nqp::atpos_i(self, ++$pos))),
  184. $subbuf)))))))
  185. }
  186. method reverse(Blob:D:) {
  187. my int $elems = nqp::elems(self);
  188. my int $last = nqp::sub_i($elems,1);
  189. my $reversed := nqp::setelems(nqp::create(self),$elems);
  190. my int $i = -1;
  191. nqp::while(
  192. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  193. nqp::bindpos_i($reversed,nqp::sub_i($last,$i),
  194. nqp::atpos_i(self,$i))
  195. );
  196. $reversed
  197. }
  198. method COMPARE(Blob:D: Blob:D \other) {
  199. my $other := nqp::decont(other);
  200. my int $elems = nqp::elems(self);
  201. if nqp::cmp_i($elems,nqp::elems($other)) -> $diff {
  202. $diff
  203. }
  204. else {
  205. my int $i = -1;
  206. return nqp::cmp_i(nqp::atpos_i(self,$i),nqp::atpos_i($other,$i))
  207. if nqp::cmp_i(nqp::atpos_i(self,$i),nqp::atpos_i($other,$i))
  208. while nqp::islt_i(++$i,$elems);
  209. 0
  210. }
  211. }
  212. method SAME(Blob:D: Blob:D \other) {
  213. my $other := nqp::decont(other);
  214. my int $elems = nqp::elems(self);
  215. return False unless nqp::iseq_i($elems,nqp::elems($other));
  216. my int $i = -1;
  217. return False
  218. unless nqp::iseq_i(nqp::atpos_i(self,$i),nqp::atpos_i($other,$i))
  219. while nqp::islt_i(++$i,$elems);
  220. True
  221. }
  222. method join(Blob:D: $delim = '') {
  223. my int $elems = nqp::elems(self);
  224. my $list := nqp::setelems(nqp::list_s,$elems);
  225. my int $i = -1;
  226. nqp::bindpos_s($list,$i,
  227. nqp::tostr_I(nqp::p6box_i(nqp::atpos_i(self,$i))))
  228. while nqp::islt_i(++$i,$elems);
  229. nqp::join($delim.Str,$list)
  230. }
  231. proto method unpack(|) {*}
  232. multi method unpack(Blob:D: Str:D $template) {
  233. nqp::isnull(nqp::getlexcaller('EXPERIMENTAL-PACK')) and X::Experimental.new(
  234. feature => "the 'unpack' method",
  235. use => "pack"
  236. ).throw;
  237. self.unpack($template.comb(/<[a..zA..Z]>[\d+|'*']?/))
  238. }
  239. multi method unpack(Blob:D: @template) {
  240. nqp::isnull(nqp::getlexcaller('EXPERIMENTAL-PACK')) and X::Experimental.new(
  241. feature => "the 'unpack' method",
  242. use => "pack"
  243. ).throw;
  244. my @bytes = self.list;
  245. my @fields;
  246. for @template -> $unit {
  247. my $directive = substr($unit,0,1);
  248. my $amount = substr($unit,1);
  249. my $pa = $amount eq '' ?? 1 !!
  250. $amount eq '*' ?? @bytes.elems !! +$amount;
  251. given $directive {
  252. when 'a' | 'A' | 'Z' {
  253. @fields.push: @bytes.splice(0, $pa).map(&chr).join;
  254. }
  255. when 'H' {
  256. my str $hexstring = '';
  257. for ^$pa {
  258. my $byte = shift @bytes;
  259. $hexstring ~= ($byte +> 4).fmt('%x')
  260. ~ ($byte % 16).fmt('%x');
  261. }
  262. @fields.push($hexstring);
  263. }
  264. when 'x' {
  265. splice @bytes, 0, $pa;
  266. }
  267. when 'C' {
  268. @fields.append: @bytes.splice(0, $pa);
  269. }
  270. when 'S' | 'v' {
  271. for ^$pa {
  272. last if @bytes.elems < 2;
  273. @fields.append: shift(@bytes)
  274. + (shift(@bytes) +< 0x08);
  275. }
  276. }
  277. when 'L' | 'V' {
  278. for ^$pa {
  279. last if @bytes.elems < 4;
  280. @fields.append: shift(@bytes)
  281. + (shift(@bytes) +< 0x08)
  282. + (shift(@bytes) +< 0x10)
  283. + (shift(@bytes) +< 0x18);
  284. }
  285. }
  286. when 'n' {
  287. for ^$pa {
  288. last if @bytes.elems < 2;
  289. @fields.append: (shift(@bytes) +< 0x08)
  290. + shift(@bytes);
  291. }
  292. }
  293. when 'N' {
  294. for ^$pa {
  295. last if @bytes.elems < 4;
  296. @fields.append: (shift(@bytes) +< 0x18)
  297. + (shift(@bytes) +< 0x10)
  298. + (shift(@bytes) +< 0x08)
  299. + shift(@bytes);
  300. }
  301. }
  302. X::Buf::Pack.new(:$directive).throw;
  303. }
  304. }
  305. return |@fields;
  306. }
  307. # XXX: the pack.t spectest file seems to require this method
  308. # not sure if it should be changed to list there...
  309. method contents(Blob:D:) { self.list }
  310. method encoding() { Any }
  311. method !push-list(\action,\to,\from) {
  312. if nqp::istype(from,List) {
  313. my Mu $from := nqp::getattr(from,List,'$!reified');
  314. if nqp::defined($from) {
  315. my int $elems = nqp::elems($from);
  316. my int $j = nqp::elems(to);
  317. nqp::setelems(to, $j + $elems); # presize for efficiency
  318. my int $i = -1;
  319. my $got;
  320. nqp::while(
  321. nqp::islt_i(++$i,$elems),
  322. nqp::stmts(
  323. ($got := nqp::atpos($from,$i)),
  324. nqp::istype(nqp::hllize($got),Int)
  325. ?? nqp::bindpos_i(to,$j++,$got)
  326. !! self!fail-typecheck-element(action,$i,$got).throw))
  327. }
  328. }
  329. else {
  330. my $iter := from.iterator;
  331. my int $i = 0;
  332. my $got;
  333. until ($got := $iter.pull-one) =:= IterationEnd {
  334. nqp::istype($got,Int)
  335. ?? nqp::push_i(to,$got)
  336. !! self!fail-typecheck-element(action,$i,$got).throw;
  337. ++$i;
  338. }
  339. }
  340. to
  341. }
  342. method !unshift-list(\action,\to,\from) {
  343. if nqp::istype(from,List) {
  344. my Mu $from := nqp::getattr(from,List,'$!reified');
  345. if nqp::defined($from) {
  346. my int $i = nqp::elems($from);
  347. nqp::istype((my $got := nqp::atpos($from,$i)),Int)
  348. ?? nqp::unshift_i(to,$got)
  349. !! self!fail-typecheck-element(action,$i,$got).throw
  350. while nqp::isge_i(--$i,0);
  351. }
  352. to
  353. }
  354. else {
  355. nqp::splice(to,self!push-list(action,nqp::create(self),from),0,0)
  356. }
  357. }
  358. method !spread(\to,\from) {
  359. if nqp::elems(from) -> int $values { # something to init with
  360. my int $elems = nqp::elems(to) - $values;
  361. my int $i = -$values;
  362. nqp::splice(to,from,$i,$values)
  363. while nqp::isle_i($i = $i + $values,$elems);
  364. if nqp::isgt_i($i,$elems) { # something left to init
  365. --$i; # went one too far
  366. $elems = $elems + $values;
  367. my int $j = -1;
  368. nqp::bindpos_i(to,$i,nqp::atpos_i(from,$j = ($j + 1) % $values))
  369. while nqp::islt_i(++$i,$elems);
  370. }
  371. }
  372. to
  373. }
  374. method !fail-range($got) {
  375. Failure.new(X::OutOfRange.new(
  376. :what($*INDEX // 'Index'),
  377. :$got,
  378. :range("0..{nqp::elems(self)-1}")
  379. ))
  380. }
  381. method !fail-typecheck-element(\action,\i,\got) {
  382. self!fail-typecheck(action ~ "ing element #" ~ i,got);
  383. }
  384. method !fail-typecheck($action,$got) {
  385. Failure.new(X::TypeCheck.new(
  386. operation => $action ~ " to " ~ self.^name,
  387. got => $got,
  388. expected => T,
  389. ))
  390. }
  391. }
  392. constant blob8 = Blob[uint8];
  393. constant blob16 = Blob[uint16];
  394. constant blob32 = Blob[uint32];
  395. constant blob64 = Blob[uint64];
  396. my class utf8 does Blob[uint8] is repr('VMArray') {
  397. multi method decode(utf8:D: $encoding) {
  398. my $enc = Rakudo::Internals.NORMALIZE_ENCODING($encoding);
  399. die "Can not decode a utf-8 buffer as if it were $encoding"
  400. unless $enc eq 'utf8';
  401. nqp::p6box_s(nqp::decode(self, 'utf8'))
  402. }
  403. method encoding() { 'utf-8' }
  404. multi method Str(utf8:D:) { self.decode }
  405. multi method Stringy(utf8:D:) { self.decode }
  406. }
  407. my class utf16 does Blob[uint16] is repr('VMArray') {
  408. multi method decode(utf16:D: $encoding = 'utf-16') {
  409. my $enc = Rakudo::Internals.NORMALIZE_ENCODING($encoding);
  410. die "Can not decode a utf-16 buffer as if it were $encoding"
  411. unless $enc eq 'utf16';
  412. nqp::p6box_s(nqp::decode(self, 'utf16'))
  413. }
  414. method encoding() { 'utf-16' }
  415. multi method Str(utf16:D:) { self.decode }
  416. multi method Stringy(utf16:D:) { self.decode }
  417. }
  418. my class utf32 does Blob[uint32] is repr('VMArray') {
  419. multi method decode(utf32:D: $encoding = 'utf-32') {
  420. my $enc = Rakudo::Internals.NORMALIZE_ENCODING($encoding);
  421. die "Can not decode a utf-32 buffer as if it were $encoding"
  422. unless $enc eq 'utf32';
  423. nqp::p6box_s(nqp::decode(self, 'utf32'))
  424. }
  425. method encoding() { 'utf-32' }
  426. multi method Str(utf32:D:) { self.decode }
  427. multi method Stringy(utf32:D:) { self.decode }
  428. }
  429. my role Buf[::T = uint8] does Blob[T] is repr('VMArray') is array_type(T) {
  430. multi method WHICH(Buf:D:) { self.Mu::WHICH }
  431. multi method AT-POS(Buf:D: int \pos) is raw {
  432. nqp::islt_i(pos,0)
  433. ?? Failure.new(X::OutOfRange.new(
  434. :what($*INDEX // 'Index'),:got(pos),:range<0..^Inf>))
  435. !! nqp::atposref_i(self, pos)
  436. }
  437. multi method AT-POS(Buf:D: Int:D \pos) is raw {
  438. my int $pos = nqp::unbox_i(pos);
  439. nqp::islt_i($pos,0)
  440. ?? Failure.new(X::OutOfRange.new(
  441. :what($*INDEX // 'Index'),:got(pos),:range<0..^Inf>))
  442. !! nqp::atposref_i(self,$pos)
  443. }
  444. multi method ASSIGN-POS(Buf:D: int \pos, Mu \assignee) {
  445. nqp::islt_i(pos,0)
  446. ?? Failure.new(X::OutOfRange.new(
  447. :what($*INDEX // 'Index'),:got(pos),:range<0..^Inf>))
  448. !! nqp::bindpos_i(self,pos,assignee)
  449. }
  450. multi method ASSIGN-POS(Buf:D: Int:D \pos, Mu \assignee) {
  451. my int $pos = nqp::unbox_i(pos);
  452. nqp::islt_i($pos,0)
  453. ?? Failure.new(X::OutOfRange.new(
  454. :what($*INDEX // 'Index'),:got(pos),:range<0..^Inf>))
  455. !! nqp::bindpos_i(self,$pos,assignee)
  456. }
  457. multi method list(Buf:D:) {
  458. Seq.new(class :: does Rakudo::Iterator::Blobby {
  459. method pull-one() is raw {
  460. nqp::if(
  461. nqp::islt_i(($!i = nqp::add_i($!i,1)),nqp::elems($!blob)),
  462. nqp::atposref_i($!blob,$!i),
  463. IterationEnd
  464. )
  465. }
  466. }.new(self)).cache
  467. }
  468. proto method pop(|) { * }
  469. multi method pop(Buf:D:) {
  470. nqp::elems(self)
  471. ?? nqp::pop_i(self)
  472. !! Failure.new(X::Cannot::Empty.new(:action<pop>,:what(self.^name)))
  473. }
  474. proto method shift(|) { * }
  475. multi method shift(Buf:D:) {
  476. nqp::elems(self)
  477. ?? nqp::shift_i(self)
  478. !! Failure.new(X::Cannot::Empty.new(:action<shift>,:what(self.^name)))
  479. }
  480. method reallocate(Buf:D: Int:D $elements) { nqp::setelems(self,$elements) }
  481. my $empty := nqp::list_i;
  482. proto method splice(|) { * }
  483. multi method splice(Buf:D \SELF:) { my $buf = SELF; SELF = Buf.new; $buf }
  484. multi method splice(Buf:D: Int:D $offset, $size = Whatever) {
  485. my int $remove = self!remove($offset,$size);
  486. my $result := $remove
  487. ?? self.subbuf($offset,$remove) # until something smarter
  488. !! nqp::create(self);
  489. nqp::splice(self,$empty,$offset,$remove);
  490. $result
  491. }
  492. multi method splice(Buf:D: Int:D $offset, $size, int $got) {
  493. self!splice-native($offset,$size,$got)
  494. }
  495. multi method splice(Buf:D: Int:D $offset, $size, Int:D $got) {
  496. self!splice-native($offset,$size,$got)
  497. }
  498. multi method splice(Buf:D: Int:D $offset, $size, Mu:D $got) {
  499. self!fail-typecheck('splice',$got)
  500. }
  501. multi method splice(Buf:D: Int:D $offset, $size, Buf:D $buf) {
  502. self!splice-native($offset,$size,$buf)
  503. }
  504. multi method splice(Buf:D: Int:D $offset, $size, int @values) {
  505. self!splice-native($offset,$size,@values)
  506. }
  507. multi method splice(Buf:D: Int:D $offset, $size, @values) {
  508. self!splice-native($offset,$size,
  509. self!push-list("splic",nqp::create(self),@values))
  510. }
  511. method !remove(\offset,\size) {
  512. nqp::istype(size,Whatever)
  513. ?? nqp::elems(self) - offset
  514. !! nqp::istype(size,Int)
  515. ?? size
  516. !! size.Int
  517. }
  518. method !splice-native(Buf:D: Int:D $offset, $size, \x) {
  519. my int $remove = self!remove($offset,$size);
  520. my $result := $remove
  521. ?? self.subbuf($offset,$remove) # until something smarter
  522. !! nqp::create(self);
  523. nqp::splice(
  524. self,nqp::islist(x) ?? x !! nqp::list_i(x),$offset,$remove);
  525. $result
  526. }
  527. proto method push(|) { * }
  528. multi method push(Buf:D: int $got) { nqp::push_i(self,$got); self }
  529. multi method push(Buf:D: Int:D $got) { nqp::push_i(self,$got); self }
  530. multi method push(Buf:D: Mu:D $got) { self!fail-typecheck('push',$got) }
  531. multi method push(Buf:D: Blob:D $buf) {
  532. nqp::splice(self,$buf,nqp::elems(self),0)
  533. }
  534. multi method push(Buf:D: **@values) { self!pend(@values,'push') }
  535. proto method append(|) { * }
  536. multi method append(Buf:D: int $got) { nqp::push_i(self,$got); self }
  537. multi method append(Buf:D: Int:D $got) { nqp::push_i(self,$got); self }
  538. multi method append(Buf:D: Mu:D $got) { self!fail-typecheck('append',$got) }
  539. multi method append(Buf:D: Blob:D $buf) {
  540. nqp::splice(self,$buf,nqp::elems(self),0)
  541. }
  542. multi method append(Buf:D: int @values) {
  543. nqp::splice(self,@values,nqp::elems(self),0)
  544. }
  545. multi method append(Buf:D: @values) { self!pend(@values,'append') }
  546. multi method append(Buf:D: *@values) { self!pend(@values,'append') }
  547. proto method unshift(|) { * }
  548. multi method unshift(Buf:D: int $got) { nqp::unshift_i(self,$got); self }
  549. multi method unshift(Buf:D: Int:D $got) { nqp::unshift_i(self,$got); self }
  550. multi method unshift(Buf:D: Mu:D $got) { self!fail-typecheck('unshift',$got) }
  551. multi method unshift(Buf:D: Blob:D $buf) { nqp::splice(self,$buf,0,0) }
  552. multi method unshift(Buf:D: **@values) { self!pend(@values,'unshift') }
  553. proto method prepend(|) { * }
  554. multi method prepend(Buf:D: int $got) { nqp::unshift_i(self,$got); self }
  555. multi method prepend(Buf:D: Int:D $got) { nqp::unshift_i(self,$got); self }
  556. multi method prepend(Buf:D: Mu:D $got) { self!fail-typecheck('prepend',$got) }
  557. multi method prepend(Buf:D: Blob:D $buf) { nqp::splice(self,$buf,0,0) }
  558. multi method prepend(Buf:D: int @values) { nqp::splice(self,@values,0,0) }
  559. multi method prepend(Buf:D: @values) { self!pend(@values,'prepend') }
  560. multi method prepend(Buf:D: *@values) { self!pend(@values,'prepend') }
  561. method !pend(Buf:D: @values, $action) {
  562. @values.is-lazy
  563. ?? Failure.new(X::Cannot::Lazy.new(:$action,:what(self.^name)))
  564. !! $action eq 'push' || $action eq 'append'
  565. ?? self!push-list($action,self,@values)
  566. !! self!unshift-list($action,self,@values)
  567. }
  568. method subbuf-rw($from = 0, $elems = self.elems - $from) is rw {
  569. my Blob $subbuf = self.subbuf($from, $elems);
  570. Proxy.new(
  571. FETCH => sub ($) { $subbuf },
  572. STORE => sub ($, Blob:D $new) {
  573. nqp::splice(nqp::decont(self),nqp::decont($new),$from,$elems)
  574. }
  575. );
  576. }
  577. }
  578. constant buf8 = Buf[uint8];
  579. constant buf16 = Buf[uint16];
  580. constant buf32 = Buf[uint32];
  581. constant buf64 = Buf[uint64];
  582. proto sub pack(|) {*}
  583. multi sub pack(Str $template, *@items) {
  584. nqp::isnull(nqp::getlexcaller('EXPERIMENTAL-PACK')) and X::Experimental.new(
  585. feature => "the 'pack' function",
  586. use => "pack"
  587. ).throw;
  588. pack($template.comb(/<[a..zA..Z]>[\d+|'*']?/), @items)
  589. }
  590. multi sub pack(@template, *@items) {
  591. nqp::isnull(nqp::getlexcaller('EXPERIMENTAL-PACK')) and X::Experimental.new(
  592. feature => "the 'pack' function",
  593. use => "pack"
  594. ).throw;
  595. my @bytes;
  596. for @template -> $unit {
  597. my $directive = substr($unit,0,1);
  598. my $amount = substr($unit,1);
  599. given $directive {
  600. when 'A' {
  601. my $ascii = shift @items // '';
  602. my $data = $ascii.ords.cache;
  603. if $amount eq '*' {
  604. $amount = $data.elems;
  605. }
  606. if $amount eq '' {
  607. $amount = 1;
  608. }
  609. for (@$data, 0x20 xx *).flat[^$amount] -> $byte {
  610. X::Buf::Pack::NonASCII.new(:char($byte.chr)).throw if $byte > 0x7f;
  611. @bytes.push: $byte;
  612. }
  613. }
  614. when 'a' {
  615. my $data = shift @items // Buf.new;
  616. $data.=encode if nqp::istype($data,Str);
  617. if $amount eq '*' {
  618. $amount = $data.elems;
  619. }
  620. if $amount eq '' {
  621. $amount = 1;
  622. }
  623. for (@$data, 0 xx *).flat[^$amount] -> $byte {
  624. @bytes.push: $byte;
  625. }
  626. }
  627. when 'H' {
  628. my $hexstring = shift @items // '';
  629. if $hexstring.chars % 2 {
  630. $hexstring ~= '0';
  631. }
  632. @bytes.append: map { :16($_) }, $hexstring.comb(/../);
  633. }
  634. when 'x' {
  635. if $amount eq '*' {
  636. $amount = 0;
  637. }
  638. elsif $amount eq '' {
  639. $amount = 1;
  640. }
  641. @bytes.append: 0x00 xx $amount;
  642. }
  643. when 'C' {
  644. my $number = shift(@items);
  645. @bytes.push: $number % 0x100;
  646. }
  647. when 'S' | 'v' {
  648. my $number = shift(@items);
  649. @bytes.append: ($number, $number +> 0x08) >>%>> 0x100;
  650. }
  651. when 'L' | 'V' {
  652. my $number = shift(@items);
  653. @bytes.append: ($number, $number +> 0x08,
  654. $number +> 0x10, $number +> 0x18) >>%>> 0x100;
  655. }
  656. when 'n' {
  657. my $number = shift(@items);
  658. @bytes.append: ($number +> 0x08, $number) >>%>> 0x100;
  659. }
  660. when 'N' {
  661. my $number = shift(@items);
  662. @bytes.append: ($number +> 0x18, $number +> 0x10,
  663. $number +> 0x08, $number) >>%>> 0x100;
  664. }
  665. X::Buf::Pack.new(:$directive).throw;
  666. }
  667. }
  668. return Buf.new(@bytes);
  669. }
  670. multi sub infix:<~>(Blob:D \a) { a }
  671. multi sub infix:<~>(Blob:D $a, Blob:D $b) {
  672. my $res := nqp::create(nqp::eqaddr($a.WHAT,$b.WHAT) ?? $a !! Buf.^pun);
  673. my $adc := nqp::decont($a);
  674. my $bdc := nqp::decont($b);
  675. my int $alen = nqp::elems($adc);
  676. my int $blen = nqp::elems($bdc);
  677. nqp::setelems($res, $alen + $blen);
  678. nqp::splice($res, $adc, 0, $alen);
  679. nqp::splice($res, $bdc, $alen, $blen);
  680. }
  681. multi sub prefix:<~^>(Blob:D \a) {
  682. my $a := nqp::decont(a);
  683. my int $elems = nqp::elems($a);
  684. my $r := nqp::create($a);
  685. nqp::setelems($a,$elems);
  686. my int $i = -1;
  687. nqp::bindpos_i($r,$i,nqp::bitneg_i(nqp::atpos_i($a,$i)))
  688. while nqp::islt_i(++$i,$elems);
  689. $r
  690. }
  691. multi sub infix:<~&>(Blob:D \a, Blob:D \b) {
  692. my $a := nqp::decont(a);
  693. my $b := nqp::decont(b);
  694. my int $elemsa = nqp::elems($a);
  695. my int $elemsb = nqp::elems($b);
  696. my int $do = $elemsa > $elemsb ?? $elemsb !! $elemsa;
  697. my int $max = $elemsa > $elemsb ?? $elemsa !! $elemsb;
  698. my $r := nqp::create($a);
  699. nqp::setelems($r,$max);
  700. my int $i = -1;
  701. nqp::bindpos_i($r,$i,
  702. nqp::bitand_i(nqp::atpos_i($a,$i),nqp::atpos_i($b,$i)))
  703. while nqp::islt_i(++$i,$do);
  704. --$i; # went one too far
  705. nqp::bindpos_i($r,$i,0) while nqp::islt_i(++$i,$max);
  706. $r
  707. }
  708. multi sub infix:<~|>(Blob:D \a, Blob:D \b) {
  709. my $a := nqp::decont(a);
  710. my $b := nqp::decont(b);
  711. my int $elemsa = nqp::elems($a);
  712. my int $elemsb = nqp::elems($b);
  713. my int $do = $elemsa > $elemsb ?? $elemsb !! $elemsa;
  714. my int $max = $elemsa > $elemsb ?? $elemsa !! $elemsb;
  715. my $from := $elemsa > $elemsb ?? $a !! $b;
  716. my $r := nqp::create($a);
  717. nqp::setelems($r,$max);
  718. my int $i = -1;
  719. nqp::bindpos_i($r,$i,
  720. nqp::bitor_i(nqp::atpos_i($a,$i),nqp::atpos_i($b,$i)))
  721. while nqp::islt_i(++$i,$do);
  722. $i = $i - 1; # went one too far
  723. nqp::bindpos_i($r,$i,nqp::atpos_i($from,$i))
  724. while nqp::islt_i(++$i,$max);
  725. $r
  726. }
  727. multi sub infix:<~^>(Blob:D \a, Blob:D \b) {
  728. my $a := nqp::decont(a);
  729. my $b := nqp::decont(b);
  730. my int $elemsa = nqp::elems($a);
  731. my int $elemsb = nqp::elems($b);
  732. my int $do = $elemsa > $elemsb ?? $elemsb !! $elemsa;
  733. my int $max = $elemsa > $elemsb ?? $elemsa !! $elemsb;
  734. my $from := $elemsa > $elemsb ?? $a !! $b;
  735. my $r := nqp::create($a);
  736. nqp::setelems($r,$max);
  737. my int $i = -1;
  738. nqp::bindpos_i($r,$i,
  739. nqp::bitxor_i(nqp::atpos_i($a,$i),nqp::atpos_i($b,$i)))
  740. while nqp::islt_i(++$i,$do);
  741. --$i; # went one too far
  742. nqp::bindpos_i($r,$i,nqp::atpos_i($from,$i))
  743. while nqp::islt_i(++$i,$max);
  744. $r
  745. }
  746. multi sub infix:<eqv>(Blob:D \a, Blob:D \b) {
  747. nqp::p6bool(nqp::eqaddr(a,b) || (nqp::eqaddr(a.WHAT,b.WHAT) && a.SAME(b)))
  748. }
  749. multi sub infix:<cmp>(Blob:D \a, Blob:D \b) { ORDER(a.COMPARE(b)) }
  750. multi sub infix:<eq> (Blob:D \a, Blob:D \b) { a =:= b || a.SAME(b) }
  751. multi sub infix:<ne> (Blob:D \a, Blob:D \b) { !(a =:= b || a.SAME(b)) }
  752. multi sub infix:<lt> (Blob:D \a, Blob:D \b) { a.COMPARE(b) == -1 }
  753. multi sub infix:<gt> (Blob:D \a, Blob:D \b) { a.COMPARE(b) == 1 }
  754. multi sub infix:<le> (Blob:D \a, Blob:D \b) { a.COMPARE(b) != 1 }
  755. multi sub infix:<ge> (Blob:D \a, Blob:D \b) { a.COMPARE(b) != -1 }
  756. proto sub subbuf-rw(|) {*}
  757. multi sub subbuf-rw(Buf:D \b) is rw {
  758. b.subbuf-rw(0, b.elems);
  759. }
  760. multi sub subbuf-rw(Buf:D \b, Int() $from) is rw {
  761. b.subbuf-rw($from, b.elems - $from)
  762. }
  763. multi sub subbuf-rw(Buf:D \b, $from, $elems) is rw {
  764. b.subbuf-rw($from, $elems)
  765. }