use nqp; unit module NativeCall::Types; role ExplicitlyManagedString { has $.cstr is rw; } sub nativecast($target-type, $source) { nqp::nativecallcast(nqp::decont($target-type), nqp::decont(map_return_type($target-type)), nqp::decont($source)); } our native long is Int is ctype("long") is repr("P6int") { }; our native longlong is Int is ctype("longlong") is repr("P6int") { }; our native ulong is Int is ctype("long") is unsigned is repr("P6int") { }; our native ulonglong is Int is ctype("longlong") is unsigned is repr("P6int") { }; our native size_t is Int is ctype("size_t") is unsigned is repr("P6int") { }; our native ssize_t is Int is ctype("size_t") is repr("P6int") { }; our native bool is Int is ctype("bool") is repr("P6int") { }; our class void is repr('Uninstantiable') { }; # Expose a Pointer class for working with raw pointers. our class Pointer is repr('CPointer') { method of() { void } multi method new() { self.CREATE() } multi method new(int $addr) { nqp::box_i($addr, ::?CLASS) } multi method new(Int $addr) { nqp::box_i(nqp::unbox_i(nqp::decont($addr)), ::?CLASS) } proto method Numeric() {*} multi method Numeric(::?CLASS:U: --> 0) { } multi method Numeric(::?CLASS:D:) { self.Int } proto method Int() {*} multi method Int(::?CLASS:U: --> 0) { } multi method Int(::?CLASS:D:) { nqp::p6box_i(nqp::unbox_i(self)) } proto method Bool() {*} multi method Bool(::?CLASS:U: --> False) { } multi method Bool(::?CLASS:D:) { so self.Int } method deref(::?CLASS:D \ptr:) { self ?? nativecast(void, ptr) !! fail("Can't dereference a Null Pointer") } multi method gist(::?CLASS:U:) { '(' ~ self.^name ~ ')' } multi method gist(::?CLASS:D:) { if self.Int -> $addr { self.^name ~ '<' ~ $addr.fmt('%#x') ~ '>' } else { self.^name ~ '' } } multi method raku(::?CLASS:U:) { self.^name } multi method raku(::?CLASS:D:) { self.^name ~ '.new(' ~ self.Int ~ ')' } my role TypedPointer[::TValue] { method of() { TValue } method deref(::?CLASS:D \ptr:) { self ?? nativecast(TValue, ptr) !! fail("Can't dereference a Null Pointer"); } method add(Int $off) returns Pointer { die "Can't do arithmetic with a void pointer" if TValue.isa(void); nqp::box_i(self.Int + nqp::nativecallsizeof(TValue) * $off, self.WHAT); } method succ { self.add(1); } method pred { self.add(-1); } method AT-POS(Int $pos) { nqp::nativecallcast( TValue, nqp::istype(TValue, Int) ?? Int !! nqp::istype(TValue, Num) ?? Num !! TValue, nqp::box_i(nqp::unbox_i(self) + nqp::nativecallsizeof(TValue) * $pos, Pointer) ) } } method ^parameterize(Mu:U \p, Mu:U \t) { die "A typed pointer can only hold:\n" ~ " (u)int8, (u)int16, (u)int32, (u)int64, (u)long, (u)longlong, num16, num32, (s)size_t, bool, Str\n" ~ " and types with representation: CArray, CPointer, CStruct, CPPStruct and CUnion" ~ "not: {t.^name}" unless t ~~ Int|Num|Bool || t === Str|void || t.REPR eq any ; my $w := p.^mixin: TypedPointer[t.WHAT]; $w.^set_name: "{p.^name}[{t.^name}]"; $w; } } # CArray class, used to represent C arrays. our class CArray is repr('CArray') is array_type(Pointer) { method AT-POS(::?CLASS:D: $pos) { die "CArray cannot be used without a type" } my role IntTypedCArray[::TValue] does Positional[TValue] is array_type(TValue) { multi method AT-POS(::?CLASS:D \arr: $pos) is raw { nqp::atposref_i(nqp::decont(arr), $pos); } multi method AT-POS(::?CLASS:D \arr: Int $pos) is raw { nqp::atposref_i(nqp::decont(arr), $pos); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, int $assignee) { nqp::bindpos_i(nqp::decont(arr), nqp::unbox_i($pos), $assignee); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, Int $assignee) { nqp::bindpos_i(nqp::decont(arr), nqp::unbox_i($pos), nqp::unbox_i($assignee)); } multi method allocate(::?CLASS:U \type: int $elems) { my $arr := nqp::create(type); nqp::bindpos_i($arr, $elems - 1, 0); $arr; } multi method allocate(::?CLASS:U \type: Int:D $elems) { my $arr := nqp::create(type); nqp::bindpos_i($arr, $elems - 1, 0); $arr; } } my role UIntTypedCArray[::TValue] does Positional[TValue] is array_type(TValue) { multi method AT-POS(::?CLASS:D \arr: $pos) is raw { nqp::atposref_u(nqp::decont(arr), $pos); } multi method AT-POS(::?CLASS:D \arr: Int $pos) is raw { nqp::atposref_u(nqp::decont(arr), $pos); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, int $assignee) { nqp::bindpos_u(nqp::decont(arr), nqp::unbox_i($pos), $assignee); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, uint $assignee) { nqp::bindpos_u(nqp::decont(arr), nqp::unbox_i($pos), $assignee); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, Int $assignee) { nqp::bindpos_u(nqp::decont(arr), nqp::unbox_i($pos), nqp::unbox_u($assignee)); } multi method allocate(::?CLASS:U \type: int $elems) { my $arr := nqp::create(type); nqp::bindpos_u($arr, $elems - 1, 0); $arr; } multi method allocate(::?CLASS:U \type: Int:D $elems) { my $arr := nqp::create(type); nqp::bindpos_u($arr, $elems - 1, 0); $arr; } } my role NumTypedCArray[::TValue] does Positional[TValue] is array_type(TValue) { multi method AT-POS(::?CLASS:D \arr: $pos) is raw { nqp::atposref_n(nqp::decont(arr), $pos); } multi method AT-POS(::?CLASS:D \arr: Int $pos) is raw { nqp::atposref_n(nqp::decont(arr), $pos); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, num $assignee) { nqp::bindpos_n(nqp::decont(arr), nqp::unbox_i($pos), $assignee); } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, Num $assignee) { nqp::bindpos_n(nqp::decont(arr), nqp::unbox_i($pos), nqp::unbox_n($assignee)); } multi method allocate(::?CLASS:U \type: int $elems) { my $arr := nqp::create(type); nqp::bindpos_n($arr, $elems - 1, 0e0); $arr; } multi method allocate(::?CLASS:U \type: Int:D $elems) { my $arr := nqp::create(type); nqp::bindpos_n($arr, $elems - 1, 0e0); $arr; } } my role TypedCArray[::TValue] does Positional[TValue] is array_type(TValue) { multi method AT-POS(::?CLASS:D \arr: $pos) is rw { Proxy.new: FETCH => method () { nqp::atpos(nqp::decont(arr), nqp::unbox_i($pos.Int)) }, STORE => method ($v) { nqp::bindpos(nqp::decont(arr), nqp::unbox_i($pos.Int), nqp::decont($v)); self } } multi method AT-POS(::?CLASS:D \arr: Int $pos) is rw { Proxy.new: FETCH => method () { nqp::atpos(nqp::decont(arr), $pos) }, STORE => method ($v) { nqp::bindpos(nqp::decont(arr), $pos, nqp::decont($v)); self } } multi method ASSIGN-POS(::?CLASS:D \arr: Int $pos, \assignee) { nqp::bindpos(nqp::decont(arr), nqp::unbox_i($pos), nqp::decont(assignee)); } multi method allocate(::?CLASS:U: int $elems) { my $arr := nqp::create(self); my $type := ::?CLASS.^array_type; nqp::bindpos($arr, $_, nqp::create($type)) for ^$elems; $arr; } multi method allocate(::?CLASS:U: Int:D $elems) { my $arr := nqp::create(self); my $type := ::?CLASS.^array_type; nqp::bindpos($arr, $_, nqp::create($type)) for ^$elems; $arr; } } method ^parameterize(Mu:U \arr, Mu:U \t) { my $mixin; if t ~~ Int { $mixin := t.^unsigned ?? UIntTypedCArray[t.WHAT] !! IntTypedCArray[t.WHAT]; } elsif t ~~ Num { $mixin := NumTypedCArray[t.WHAT]; } else { die "A C array can only hold:\n" ~ " (u)int8, (u)int16, (u)int32, (u)int64, (u)long, (u)longlong, num16, num32, (s)size_t, bool, Str\n" ~ " and types with representation: CArray, CPointer, CStruct, CPPStruct and CUnion\n" ~ "not: {t.^name}" unless t === Str || t.REPR eq 'CStruct' | 'CPPStruct' | 'CUnion' | 'CPointer' | 'CArray'; $mixin := TypedCArray[t]; } my $what := arr.^mixin: $mixin; $what.^set_name("{arr.^name}[{t.^name}]"); $what; } method Str { self.join(' ') } method elems { nqp::elems(self) } method list { do for ^self.elems { self.AT-POS($_) } } multi method new() { nqp::create(self) } multi method new(*@values) { self.new(@values) } multi method new(@values) { if @values.elems -> $n { my int $elems = $n - 1; my $result := nqp::create(self); # XXX setelems would be nice $result.ASSIGN-POS($elems,@values.AT-POS($elems)); # fake setelems my int $i = -1; nqp::while( nqp::islt_i(($i = nqp::add_i($i,1)),$elems), $result.ASSIGN-POS($i,@values.AT-POS($i)), ); $result } else { nqp::create(self) } } } # duplicated code from NativeCall.pm to support Pointer.deref multi sub map_return_type(Mu $type) { Mu } multi sub map_return_type($type) { nqp::istype($type, Int) ?? Int !! nqp::istype($type, Num) ?? Num !! $type; } # vim: expandtab shiftwidth=4