Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Marshaling vectors of pointers

Tags:

haskell

ffi

I'm trying to represent a Haskell vector of arbitrarily right-nested pairs (i.e. Vector (Int64, (Int64, (...)))) as a 2-d array in C (i.e. int64_t**), indexed first as the vector component, then the tuple component.

Here's my C function:

void test(int64_t** y, int32_t vecSize int16_t tupSize, int64_t* tup)
{
    printf("Tup vals: ");
    for(int i = 0; i < tupSize; i++) {
        printf("%" PRId64 ",",tup[i]);
    }
    printf("\n");


    printf("vec\n");
    for(int i = 0; i < vecSize; i++) {
        printf("%d: (", i);
        for(int j = 0; j < tupSize; j++) {
            printf("%" PRId64 ",", y[i][j]);
        }
        printf(")\n");
    }
}

On the Haskell side I have:

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Int
import Data.Vector.Storable (Vector, singleton, unsafeWith)

import Foreign.Marshal.Utils (with)
import Foreign.Ptr
import Foreign.Storable (Storable (..))

foreign import ccall unsafe "test" test :: Ptr (Ptr Int64) -> Int64 -> Int16 -> Ptr Int64 -> IO ()

-- instance assumes right-nested pairs, but not enforced at type level
instance (Storable a, Storable b)
  => Storable (a,b) where
  sizeOf _ = (sizeOf (undefined :: a)) + (sizeOf (undefined :: b))
  alignment _ = max (alignment (undefined :: a)) (alignment (undefined :: b))
  peek p = do
    a <- peek (castPtr p :: Ptr a)
    b <- peek (castPtr (plusPtr p (sizeOf a)) :: Ptr b)
    return (a,b)
  poke p (a,b) = do
    poke (castPtr p :: Ptr a) a
    poke (castPtr (plusPtr p (sizeOf a)) :: Ptr b) b

main :: IO ()
main = do
  let tup = (10,11) :: (Int64, Int64)
      vec = singleton (2,3) :: Vector (Int64, Int64)
  with tup $ \tptr -> 
    unsafeWith vec $ \vptr ->
      test (castPtr vptr) 1 2 (castPtr tptr)

This prints

Moduli: 10,11,
vec
Segmentation fault

which leads me to think that my Storable (a,b) instance is fine: I'm getting a pointer for (Int64,Int64), then casting it to Ptr Int64, and reading the data just fine in C. So the question is what is going wrong with the vector? I'm attempting to do the same thing: create a Vector (Int64, Int64), get a pointer of type Ptr (Int64, Int64) for it, and cast it to a Ptr (Ptr Int64). Why am I getting a segfault when I try to access the array in C, and what is the proper way to marshal this data?

like image 610
crockeea Avatar asked Sep 26 '22 12:09

crockeea


1 Answers

You're not using the same data format on the two sides. Your Haskell code produces a flat array of all the values in all the tuples of the vector, while your C code expects an array of pointers, one for each element of the vector, pointing to the values in that tuple.

If you could declare your C function like this (maybe this is valid C nowadays, I don't know)

void test(int64_t (*y)[tupSize], int32_t vecSize, int16_t tupSize, int64_t *tup)

then C would be using the same layout as Haskell. Otherwise, you can index manually with

//  SINGLE pointer |
//                 v
void test(int64_t *y, int32_t vecSize, int16_t tupSize, int64_t *tup)
...
    printf("%" PRId64 ",", y[i*tupSize+j]);
like image 124
Reid Barton Avatar answered Oct 19 '22 23:10

Reid Barton