packages feed

jvm-batching (empty) → 0.1

raw patch · 16 files changed

+1652/−0 lines, 16 filesdep +basedep +bytestringdep +criterionbuild-type:Customsetup-changed

Dependencies added: base, bytestring, criterion, deepseq, distributed-closure, hspec, inline-java, jni, jvm, jvm-batching, singletons, split, text, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016-2017 LeapYear Technologies, Inc.++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,38 @@+# jvm-batching++Provides batched marshalling of values between Java and Haskell.++## Using it as a dependency++Add `jvm-batching` to the list of dependencies in your .cabal file.+Then edit the `Setup.hs` file to add the `jvm-batching.jar` to the+classpath.++```Haskell+import Distribution.Simple+import Language.Java.Inline.Cabal+import qualified Language.Java.Batching.Jars++main = do+    jars <- Language.Java.Batching.Jars.getJars+    defaultMainWithHooks (addJarsToClasspath jars simpleUserHooks)+```++Add a `custom-setup` stanza to your .cabal file.++```+custom-setup+  setup-depends:+    base,+    Cabal,+    inline-java,+    jvm-batching+```++## Layout of source directories++This is a multi-language package. We use+Maven's [standard directory layout][maven-sdl] to organize source code+in multiple languages side-by-side.++[maven-sdl]: https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
+ Setup.hs view
@@ -0,0 +1,4 @@+import Distribution.Simple+import Language.Java.Inline.Cabal++main = defaultMainWithHooks (gradleHooks simpleUserHooks)
+ benchmarks/Main.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main where++import Criterion.Main as Criterion+import Control.DeepSeq+import Data.Int+import qualified Data.Vector as V+import Data.List.Split (chunksOf)+import qualified Data.Vector.Storable as VS+import Language.Java+import Language.Java.Batching ()++roundtrip :: forall a. (Reify a, Reflect a) => a -> IO ()+roundtrip a = pushWithSizeHint 30 $ (reflect a >>= reify :: IO a) >> pop++intValues :: [Int32]+intValues = [1..1000]++vValues :: [VS.Vector Int32]+vValues = [ VS.fromList [i, i] | i <- [1..500] ]++benchBatching :: Benchmark+benchBatching = deepseq intValues $+    bgroup "roundtrips"+    [ bgroup "ints"+      [ bgroup "no batching"+        [ bench "list" $ nfIO $ roundtrip intValues+        , bench "storable array" $ nfIO $ roundtrip $ VS.fromList intValues+        ]+      , bgroup "batching"+        [ bench "bare" $ nfIO $ roundtrip $ V.fromList intValues+        , bench "unboxed vectors" $ nfIO $ roundtrip $+            V.fromList $ map VS.fromList $+              chunksOf (length intValues `div` 7) intValues+        , bench "boxed vectors" $ nfIO $ roundtrip $+            V.fromList $ map V.fromList $+              chunksOf (length intValues `div` 7) intValues+        ]+      ]+    , bgroup "int vectors"+      [ bgroup "no batching"+        [ bench "list" $ nfIO $ roundtrip vValues+        ]+      , bgroup "batching"+        [ bench "bare" $ nfIO $ roundtrip $ V.fromList vValues+        , bench "vectors of vectors" $ nfIO $ roundtrip $+            V.fromList $ map V.fromList $+              chunksOf (length vValues `div` 7) vValues+        ]+      ]+    ]++main :: IO ()+main = withJVM ["-Djava.class.path=build/libs/jvm-batching.jar"] $+    Criterion.defaultMain [benchBatching]
+ build.gradle view
@@ -0,0 +1,5 @@+apply plugin: 'java'++dependencies {+    compile files(buildscript.sourceFile.getParent() + "/build/libs/jvm-batching.jar")+}
+ jvm-batching.cabal view
@@ -0,0 +1,85 @@+name:                jvm-batching+version:             0.1+synopsis:            Provides batched marshalling of values between Java and Haskell.+description:         Please see README.md.+homepage:            http://github.com/tweag/inline-java/tree/master/jvm-batching#readme+license:             BSD3+license-file:        LICENSE+author:              Tweag I/O+maintainer:          facundo.dominguez@tweag.io+copyright:           2017 LeapYear Technologies+category:            FFI, JVM, Java+build-type:          Custom+cabal-version:       >=1.10+extra-source-files:+  src/main/java/io/tweag/jvm/batching/Tuple2.java+  src/main/java/io/tweag/jvm/batching/BatchWriter.java+  src/main/java/io/tweag/jvm/batching/BatchWriters.java+  src/main/java/io/tweag/jvm/batching/BatchReader.java+  src/main/java/io/tweag/jvm/batching/BatchReaders.java+  README.md+  build.gradle++source-repository head+  type: git+  location: https://github.com/tweag/inline-java+  subdir: jvm-batching++custom-setup+  setup-depends:+    base,+    Cabal >= 1.24,+    inline-java >= 0.7++library+  hs-source-dirs: src/main/haskell src/cabal/haskell+  default-language: Haskell2010+  exposed-modules:+    Language.Java.Batching+    Language.Java.Batching.Jars+  other-modules:+    Paths_jvm_batching+  build-depends:+    base >= 4.7 && < 5,+    bytestring,+    distributed-closure >= 0.3.5,+    jni >= 0.6.0 && <0.7,+    jvm >= 0.4.0 && <0.5,+    inline-java >= 0.7 && <0.9,+    singletons >= 2.2,+    text,+    vector++test-suite spec+  type:+    exitcode-stdio-1.0+  hs-source-dirs: src/test/haskell+  main-is: Main.hs+  other-modules:+    Language.Java.BatchingSpec+    Spec+  build-depends:+    base,+    bytestring,+    hspec,+    inline-java,+    jvm,+    jvm-batching,+    text,+    vector+  default-language: Haskell2010++benchmark micro-benchmarks+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs: benchmarks+  build-depends:+    base >=4.8 && <5,+    criterion,+    deepseq,+    jvm,+    jvm-batching,+    split,+    vector+  default-language: Haskell2010+  ghc-options: -threaded
+ src/cabal/haskell/Language/Java/Batching/Jars.hs view
@@ -0,0 +1,7 @@+-- | Provides the paths to the jars needed to build with jvm-batching.+module Language.Java.Batching.Jars where++import Paths_jvm_batching (getDataFileName)++getJars :: IO [String]+getJars = (:[]) <$> getDataFileName "build/libs/jvm-batching.jar"
+ src/main/haskell/Language/Java/Batching.hs view
@@ -0,0 +1,628 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StaticPointers #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}++-- | This module provides composable batched marshalling.+--+-- === Batching+--+-- Calls to Java methods via JNI are slow in general. Marshalling an array of+-- primitive values can be as slow as marshalling a single value.+--+-- Because of this, reifying an iterator or a container is best done by+-- accumulates multiple elements on the java side before passing them to the+-- Haskell side. And conversely, when reflecting an iterator or container,+-- multiple Haskell values are put together before marshalling to the Java+-- side.+--+-- Some Haskell values can be batched trivially into arrays of primitive values.+-- 'Int32' can be batched in a java @int[]@, 'Double' can be batched in a java+-- @double[]@, etc. However, other types like @Tuple2 Int32 Double@ would+-- require more primitive arrays. Values of type @Tuple2 Int32 Double@ are+-- batched in a pair of java arrays of type @int[]@ and @double[]@.+--+-- > data Tuple2 a b = Tuple2 a b+--+-- More generally, the design aims to provide composable batchers. If one knows+-- how to batch types @a@ and @b@, one can also batch @Tuple2 a b@, @[a]@,+-- @Vector a@, etc.+--+-- A reference to a batch of values in Java has the type @J (Batch a)@, where+-- @a@ is the Haskell type of the elements in the batch. e.g.+--+-- > type instance Batch Int32 = 'Array ('Prim "int")+-- > type instance Batch Double = 'Array ('Prim "double")+-- > type instance Batch (Tuple2 a b) =+-- >                 'Class "scala.Tuple2" <> '[Batch a, Batch b]+--+-- When defining batching for a new type, one needs to tell how batches are+-- represented in Java by adding a type instance to the type family @Batch@.+-- In addition, procedures for adding and extracting values from the batch+-- need to be specified on both the Haskell and the Java side.+--+-- On the Java side, batches are built using the interface+-- @io.tweag.jvm.batching.BatchWriter@. On the Haskell side, these+-- batches are read using @reifyBatch@.+--+-- > class ( ... ) => BatchReify a where+-- >   newBatchWriter+-- >     :: proxy a+-- >     -> IO (J ('Iface "io.tweag.jvm.batching.BatchWriter"+-- >                  <> [Interp a, Batch a]+-- >              )+-- >           )+-- >   reifyBatch :: J (Batch a) -> Int32 -> IO (V.Vector a)+--+-- @newReifyBatched@ produces a java object implementing the @BatchWriter@+-- interface, and @reifyBatch@ allows to read a batch created in this fashion.+--+-- Conversely, batches can be read on the Java side using the interface+-- @io.tweag.jvm.batching.BatchReader@. And on the Haskell side, these+-- batches can be created with @reflectBatch@.+--+-- > class ( ... ) => BatchReflect a where+-- >  newBatchReader+-- >    :: proxy a+-- >    -> IO (J ('Iface "io.tweag.jvm.batching.BatchReader"+-- >                 <> [Batch a, Interp a]+-- >              )+-- >          )+-- >  reflectBatch :: V.Vector a -> IO (J (Batch a))+--+-- @newBatchReader@ produces a java object implementing the @BatchReader@+-- interface, and @reflectBatch@ allows to create these batches from vectors of+-- Haskell values.+--+-- The methods of @BatchReify@ and @BatchReflect@ offer default+-- implementations which marshal elements in the batch one at a time. Taking+-- advantage of batching requires defining the methods explicitly. The default+-- implementations are useful for cases where speed is not important, for+-- instance when the iterators to reflect or reify contain a single element or+-- just a very few.+--+-- 'Vector's and 'ByteString's are batched with the follow scheme.+--+-- > type instance Batch BS.ByteString+-- >   = 'Class "io.tweag.jvm.batching.Tuple2" <>+-- >        '[ 'Array ('Prim "byte")+-- >         , 'Array ('Prim "int")+-- >         ]+--+-- We use two arrays. One of the arrays contains the result of appending all of+-- the 'ByteString's in the batch. The other array contains the offset of each+-- vector in the resulting array. See 'ArrayBatch'.+--+module Language.Java.Batching+  ( Batchable(..)+  , BatchReify(..)+  , BatchReflect(..)+    -- * Array batching+  , ArrayBatch+  ) where++import Control.Distributed.Closure.TH+import Control.Exception (bracket)+import Control.Monad (forM_, foldM)+import qualified Data.ByteString             as BS+import qualified Data.ByteString.Unsafe      as BS+import Data.Int+import Data.Singletons (SingI, Proxy(..))+import qualified Data.Text as Text+import qualified Data.Text.Foreign as Text+import qualified Data.Vector as V+import qualified Data.Vector.Mutable as VM+import qualified Data.Vector.Storable as VS+import Data.Word+import Foreign.C.Types (CChar)+import Foreign.ForeignPtr (newForeignPtr_, withForeignPtr)+import Foreign.JNI+import Foreign.Ptr+import Foreign.Storable+import Language.Java+import Language.Java.Inline++imports "io.tweag.jvm.batching.*"++-- | A class of types whose values can be marshaled in batches.+class (Interpretation a, SingI (Batch a)) => Batchable (a :: k) where+  -- | The type of java batches for reifying and reflecting values of type @a@.+  type family Batch a :: JType++-- | A class for batching reification of values.+--+-- It has a method to create a batcher that creates batches in Java, and+-- another method that refies a batch into a vector of haskell values.+--+-- The type of the batch used to appear as a class parameter but we run into+-- https://ghc.haskell.org/trac/ghc/ticket/13582+--+class Batchable a => BatchReify a where+  -- | Produces a batcher that aggregates elements of type @ty@ (such as @int@)+  -- and produces collections of type @Batch a@ (such as @int[]@).+  newBatchWriter+    :: proxy a+    -> IO (J ('Iface "io.tweag.jvm.batching.BatchWriter"+                 <> [Interp a, Batch a]+             )+          )++  -- The default implementation makes calls to the JVM for each element in the+  -- batch.+  default newBatchWriter+    :: (Batch a ~ 'Array (Interp a))+    => proxy a+    -> IO (J ('Iface "io.tweag.jvm.batching.BatchWriter"+                 <> [Interp a, Batch a]+             )+          )+  newBatchWriter _ = generic <$> [java| new BatchWriters.ObjectBatchWriter() |]++  -- | Reifies the values in a batch of type @Batch a@.+  -- Gets the batch and the amount of elements it contains.+  reifyBatch :: J (Batch a) -> Int32 -> IO (V.Vector a)++  -- The default implementation makes calls to the JVM for each element in the+  -- batch.+  default reifyBatch+    :: (Reify a, Batch a ~ 'Array (Interp a))+    => J (Batch a) -> Int32 -> IO (V.Vector a)+  reifyBatch jxs size =+      V.generateM (fromIntegral size) $ \i ->+      getObjectArrayElement jxs (fromIntegral i) >>= reify . unsafeCast++-- | Helper for reifying batches of primitive types+reifyPrimitiveBatch+  :: Storable a+  => (J ('Array ty) -> IO (Ptr a))+  -> (J ('Array ty) -> Ptr a -> IO ())+  -> J ('Array ty) -> Int32 -> IO (V.Vector a)+reifyPrimitiveBatch getArrayElements releaseArrayElements jxs size = do+    bracket (getArrayElements jxs) (releaseArrayElements jxs)+      $ V.generateM (fromIntegral size) . peekElemOff++-- | Batches of arrays of variable length+--+-- The first component is an array or batch containing the actual elements,+-- and the second component is an @int[]@ where the ith position has the+-- offset of the first position after the ith array.+type ArrayBatch ty =+    'Class "io.tweag.jvm.batching.Tuple2" <>+       '[ ty+        , 'Array ('Prim "int")+        ]++-- | Helper for reifying batches of vectors+--+-- Arrays are batched with two arrays. One of the arrays contains the result+-- of appending all of the vectors in the batch. The other array contains the+-- offset of each vector in the resulting array.+--+reifyArrayBatch+  :: forall a b ty.+     (Int32 -> J ty -> IO a) -- ^ reify the array/batch of values+                             -- (takes the amount of elements in the array)+  -> (Int -> Int -> a -> IO b) -- ^ slice at a given offset of given length of some value+  -> J (ArrayBatch ty)+  -> Int32+  -> IO (V.Vector b)+reifyArrayBatch reifyB slice batch0 batchSize = do+    result <- VM.new (fromIntegral batchSize)+    let batch = unsafeUngeneric batch0+    vecEnds <- [java| (int[])$batch._2 |] >>= reify+    let count = if VS.null vecEnds then 0 else VS.last vecEnds+    vecValues <- [java| $batch._1 |] >>= reifyB count . fromObject+    _ <- foldM (writeVector result vecEnds vecValues) 0 [0 .. batchSize - 1]+    V.unsafeFreeze result+  where+    fromObject :: JObject -> J x+    fromObject = unsafeCast++    writeVector :: VM.IOVector b -> VS.Vector Int32 -> a -> Int32 -> Int32 -> IO Int32+    writeVector mv ends values offset i32 = do+        let i = fromIntegral i32+        slice (fromIntegral offset)+              (fromIntegral $ ends VS.! i - offset) values+          >>= VM.unsafeWrite mv i+        return $ ends VS.! i++-- | A class for batching reflection of values.+--+-- It has a method to create a batch reader that reads batches in Java, and+-- another method that reflects a vector of haskell values into a batch.+--+-- We considered having the type of the batch appear as a class parameter but+-- we run into https://ghc.haskell.org/trac/ghc/ticket/13582+--+class Batchable a => BatchReflect a where+  -- | Produces a batch reader that receives collections of type @ty1@+  -- (such as @int[]@) and produces values of type @ty2@ (such as @int@).+  newBatchReader+    :: proxy a+    -> IO (J ('Iface "io.tweag.jvm.batching.BatchReader"+                 <> [Batch a, Interp a]+             )+          )++  -- The default implementation makes calls to the JVM for each element in the+  -- batch.+  default newBatchReader+    :: (Batch a ~ 'Array (Interp a))+    => proxy a+    -> IO (J ('Iface "io.tweag.jvm.batching.BatchReader"+                       <> [Batch a, Interp a]+             )+          )+  newBatchReader _ =+      generic <$> [java| new BatchReaders.ObjectBatchReader() |]++  -- | Reflects the values in a vector to a batch of type @ty@.+  reflectBatch :: V.Vector a -> IO (J (Batch a))+  -- The default implementation makes calls to the JVM for each element in the+  -- batch.+  default reflectBatch+    :: (Reflect a, Batch a ~ 'Array (Interp a))+    => V.Vector a -> IO (J (Batch a))+  reflectBatch v = do+      jxs <- newArray $ fromIntegral (V.length v)+      forM_ [0 .. V.length v - 1] $ \i ->+        withLocalRef (reflect (v V.! i))+                     (setObjectArrayElement jxs (fromIntegral i))+      return jxs++-- | Helper for reflecting batches of primitive types+reflectPrimitiveBatch+  :: forall a ty. (Storable a, IsPrimitiveType ty)+  => (J ('Array ty) -> Int32 -> Int32 -> Ptr a -> IO ())+  -> V.Vector a -> IO (J ('Array ty))+reflectPrimitiveBatch setArrayRegion v = do+    let (fptr, offset, len) = VS.unsafeToForeignPtr (V.convert v)+    withForeignPtr fptr $ \ptr -> do+      jxs <- newArray (fromIntegral len)+      let aOffset = offset * sizeOf (undefined :: a)+      setArrayRegion jxs 0 (fromIntegral len) (plusPtr ptr aOffset)+      return jxs++-- | Helper for reflecting batches of vectors+--+-- The vector type is a, and vectors are manipulated exclusively with the+-- polymorphic functions given as arguments.+--+-- Vectors are batched with two arrays. One of the arrays contains the result+-- of appending all of the vectors in the batch. The other array contains the+-- offset of each vector in the resulting array.+--+reflectArrayBatch+  :: forall a b ty.+     (b -> IO (J ty))+  -> (a -> Int) -- ^ get length+  -> ([a] -> IO b) -- ^ concat+  -> V.Vector a+  -> IO (J (ArrayBatch ty))+reflectArrayBatch reflectB getLength concatenate vecs = do+    let ends = V.convert $ V.postscanl' (+) 0 $+                V.map (fromIntegral . getLength) vecs+                  :: VS.Vector Int32+    bigvec <- concatenate $ V.toList vecs+    jvec <- reflectB bigvec+    jends <- reflect ends+    generic <$> Language.Java.new [ coerce (upcast jvec)+                                  , coerce (upcast jends)+                                  ]++withStatic [d|+  instance Batchable Bool where+    type Batch Bool = 'Array ('Prim "boolean")++  instance BatchReify Bool where+    newBatchWriter _ = [java| new BatchWriters.BooleanBatchWriter() |]+    reifyBatch jxs size = do+        let toBool w = if w == 0 then False else True+        bracket (getBooleanArrayElements jxs)+                (releaseBooleanArrayElements jxs) $+          \arr -> V.generateM (fromIntegral size)+                              ((toBool <$>) . peekElemOff arr)++  instance Batchable CChar where+    type Batch CChar = 'Array ('Prim "byte")++  instance BatchReify CChar where+    newBatchWriter _ = [java| new BatchWriters.ByteBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getByteArrayElements releaseByteArrayElements++  instance Batchable Word16 where+    type Batch Word16 = 'Array ('Prim "char")++  instance BatchReify Word16 where+    newBatchWriter _ = [java| new BatchWriters.CharacterBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getCharArrayElements releaseCharArrayElements++  instance Batchable Int16 where+    type Batch Int16 = 'Array ('Prim "short")++  instance BatchReify Int16 where+    newBatchWriter _ = [java| new BatchWriters.ShortBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getShortArrayElements releaseShortArrayElements++  instance Batchable Int32 where+    type Batch Int32 = 'Array ('Prim "int")++  instance BatchReify Int32 where+    newBatchWriter _ = [java| new BatchWriters.IntegerBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getIntArrayElements releaseIntArrayElements++  instance Batchable Int64 where+    type Batch Int64 = 'Array ('Prim "long")++  instance BatchReify Int64 where+    newBatchWriter _ = [java| new BatchWriters.LongBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getLongArrayElements releaseLongArrayElements++  instance Batchable Float where+    type Batch Float = 'Array ('Prim "float")++  instance BatchReify Float where+    newBatchWriter _ = [java| new BatchWriters.FloatBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getFloatArrayElements releaseFloatArrayElements++  instance Batchable Double where+    type Batch Double = 'Array ('Prim "double")++  instance BatchReify Double where+    newBatchWriter _ = [java| new BatchWriters.DoubleBatchWriter() |]+    reifyBatch =+      reifyPrimitiveBatch getDoubleArrayElements releaseDoubleArrayElements++  instance BatchReflect Bool where+    newBatchReader _ = [java| new BatchReaders.BooleanBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setBooleanArrayRegion+                 . V.map (\w -> if w then 1 else 0)++  instance BatchReflect CChar where+    newBatchReader _ = [java| new BatchReaders.ByteBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setByteArrayRegion++  instance BatchReflect Word16 where+    newBatchReader _ = [java| new BatchReaders.CharacterBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setCharArrayRegion++  instance BatchReflect Int16 where+    newBatchReader _ = [java| new BatchReaders.ShortBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setShortArrayRegion++  instance BatchReflect Int32 where+    newBatchReader _ = [java| new BatchReaders.IntegerBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setIntArrayRegion++  instance BatchReflect Int64 where+    newBatchReader _ = [java| new BatchReaders.LongBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setLongArrayRegion++  instance BatchReflect Float where+    newBatchReader _ = [java| new BatchReaders.FloatBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setFloatArrayRegion++  instance BatchReflect Double where+    newBatchReader _ = [java| new BatchReaders.DoubleBatchReader() |]+    reflectBatch = reflectPrimitiveBatch setDoubleArrayRegion++#if ! (__GLASGOW_HASKELL__ == 800 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1)+  instance Batchable BS.ByteString where+    type Batch BS.ByteString+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "byte")+          , 'Array ('Prim "int")+          ]++  instance Batchable (VS.Vector Word16) where+    type Batch (VS.Vector Word16)+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "char")+          , 'Array ('Prim "int")+          ]++  instance Batchable (VS.Vector Int16) where+    type Batch (VS.Vector Int16)+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "short")+          , 'Array ('Prim "int")+          ]++  instance Batchable (VS.Vector Int32) where+    type Batch (VS.Vector Int32)+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "int")+          , 'Array ('Prim "int")+          ]++  instance Batchable (VS.Vector Int64) where+    type Batch (VS.Vector Int64)+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "long")+          , 'Array ('Prim "int")+          ]++  instance Batchable (VS.Vector Float) where+    type Batch (VS.Vector Float)+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "float")+          , 'Array ('Prim "int")+          ]++  instance Batchable (VS.Vector Double) where+    type Batch (VS.Vector Double)+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "double")+          , 'Array ('Prim "int")+          ]++  instance Batchable Text.Text where+    type Batch Text.Text+      = 'Class "io.tweag.jvm.batching.Tuple2" <>+         '[ 'Array ('Prim "char")+          , 'Array ('Prim "int")+          ]++  instance BatchReify BS.ByteString where+    newBatchWriter _ = [java| new BatchWriters.ByteArrayBatchWriter() |]+    reifyBatch = reifyArrayBatch (const reify) bsUnsafeSlice+      where+        bsUnsafeSlice :: Int -> Int -> BS.ByteString -> IO BS.ByteString+        bsUnsafeSlice offset sz = return . BS.unsafeTake sz . BS.unsafeDrop offset++  instance BatchReify (VS.Vector Word16) where+    newBatchWriter _ = [java| new BatchWriters.CharArrayBatchWriter() |]+    reifyBatch =+        reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++  instance BatchReify (VS.Vector Int16) where+    newBatchWriter _ = [java| new BatchWriters.ShortArrayBatchWriter() |]+    reifyBatch =+        reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++  instance BatchReify (VS.Vector Int32) where+    newBatchWriter _ = [java| new BatchWriters.IntArrayBatchWriter() |]+    reifyBatch =+        reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++  instance BatchReify (VS.Vector Int64) where+    newBatchWriter _ = [java| new BatchWriters.LongArrayBatchWriter() |]+    reifyBatch =+        reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++  instance BatchReify (VS.Vector Float) where+    newBatchWriter _ = [java| new BatchWriters.FloatArrayBatchWriter() |]+    reifyBatch =+        reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++  instance BatchReify (VS.Vector Double) where+    newBatchWriter _ = [java| new BatchWriters.DoubleArrayBatchWriter() |]+    reifyBatch =+        reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++  instance BatchReify Text.Text where+    newBatchWriter _ = [java| new BatchWriters.StringArrayBatchWriter() |]+    reifyBatch = reifyArrayBatch (const reify) $ \o n vs ->+                  (VS.unsafeWith (VS.unsafeSlice o n vs) $ \ptr ->+                      Text.fromPtr ptr (fromIntegral n)+                  )++  instance BatchReflect BS.ByteString where+    newBatchReader _ = [java| new BatchReaders.ByteArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect BS.length (return . BS.concat)++  instance BatchReflect (VS.Vector Word16) where+    newBatchReader _ = [java| new BatchReaders.CharArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++  instance BatchReflect (VS.Vector Int16) where+    newBatchReader _ = [java| new BatchReaders.ShortArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++  instance BatchReflect (VS.Vector Int32) where+    newBatchReader _ = [java| new BatchReaders.IntArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++  instance BatchReflect (VS.Vector Int64) where+    newBatchReader _ = [java| new BatchReaders.LongArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++  instance BatchReflect (VS.Vector Float) where+    newBatchReader _ = [java| new BatchReaders.FloatArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++  instance BatchReflect (VS.Vector Double) where+    newBatchReader _ = [java| new BatchReaders.DoubleArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++  instance BatchReflect Text.Text where+    newBatchReader _ = [java| new BatchReaders.StringArrayBatchReader() |]+    reflectBatch = reflectArrayBatch reflect Text.length $ \ts ->+                     Text.useAsPtr (Text.concat ts) $ \ptr len ->+                       (`VS.unsafeFromForeignPtr0` fromIntegral len)+                         <$> newForeignPtr_ ptr+#endif++  instance Interpretation a => Interpretation (V.Vector a) where+    type Interp (V.Vector a) = 'Array (Interp a)++  -- TODO: Fix GHC so it doesn't complain that variables used exclusively in+  -- quasiquotes are unused. Thus we can stop prepending variable names with+  -- '_'.++  instance (Interpretation a, BatchReify a)+           => Reify (V.Vector a) where+    reify jv = do+        _batcher <- unsafeUngeneric <$> newBatchWriter (Proxy :: Proxy a)+        n <- getArrayLength jv+        let _jvo = arrayUpcast jv+        batch <- [java| {+          $_batcher.start($n);+          for(int i=0;i<$_jvo.length;i++)+             $_batcher.set(i, $_jvo[i]);+          return $_batcher.getBatch();+          } |]+        reifyBatch (fromObject batch) n+      where+        fromObject :: JObject -> J x+        fromObject = unsafeCast++  instance (Interpretation a, BatchReflect a)+           => Reflect (V.Vector a) where+    reflect v = do+        _batch <- upcast <$> reflectBatch v+        _batchReader <-+          unsafeUngeneric <$> newBatchReader (Proxy :: Proxy a)+        jv <- [java| {+          $_batchReader.setBatch($_batch);+          return $_batchReader.getSize();+          } |] >>= newArray :: IO (J ('Array (Interp a)))+        let _jvo = arrayUpcast jv+        () <- [java| {+          for(int i=0;i<$_jvo.length;i++)+            $_jvo[i] = $_batchReader.get(i);+          } |]+        return jv++  instance Batchable a => Batchable (V.Vector a) where+    type Batch (V.Vector a) = ArrayBatch (Batch a)++  instance (SingI (Interp a), SingI (Batch a), BatchReify a)+           => BatchReify (V.Vector a) where+    newBatchWriter _ = do+        _b <- unsafeUngeneric <$> newBatchWriter (Proxy :: Proxy a)+        generic <$> [java| new BatchWriters.ObjectArrayBatchWriter($_b) |]+    reifyBatch =+        reifyArrayBatch (flip reifyBatch) (fmap (fmap return) . V.unsafeSlice)++  instance (SingI (Interp a), SingI (Batch a), BatchReflect a)+           => BatchReflect (V.Vector a) where+    newBatchReader _ = do+        _b <- unsafeUngeneric <$> newBatchReader (Proxy :: Proxy a)+        generic <$> [java| new BatchReaders.ObjectArrayBatchReader($_b) |]+    reflectBatch =+        reflectArrayBatch reflectBatch V.length (return . V.concat)+ |]
+ src/main/java/io/tweag/jvm/batching/BatchReader.java view
@@ -0,0 +1,16 @@+package io.tweag.jvm.batching;++/**+ *  A BatchReader<A, B> provides access to the elements of type B in a+ *  batch of type A.+ */+public interface BatchReader<A, B> {+    /// Yields the batch size.+    public int getSize();+    /// Yields the object at position i of the batch.+    public B get(int i);+    /// Sets the batch read by get.+    public void setBatch(A o);+    /// Yields the class of the batched objects.+    public Class<B> getElemClass();+}
+ src/main/java/io/tweag/jvm/batching/BatchReaders.java view
@@ -0,0 +1,304 @@+package io.tweag.jvm.batching;++/**+ *  Various BatchReaders+ * */+public class BatchReaders {++    public static final class BooleanBatchReader implements BatchReader<boolean[], Boolean> {+        private boolean[] arr = new boolean[0];+        public int getSize() { return arr.length; };+        public Boolean get(int i) { return arr[i]; };+        public void setBatch(boolean[] b) { arr = b; };+        public Class<Boolean> getElemClass() { return Boolean.class; };+    }++    public static final class ByteBatchReader implements BatchReader<byte[], Byte> {+        private byte[] arr = new byte[0];+        public int getSize() { return arr.length; };+        public Byte get(int i) { return arr[i]; };+        public void setBatch(byte[] b) { arr = b; };+        public Class<Byte> getElemClass() { return Byte.class; };+    }++    public static final class CharacterBatchReader implements BatchReader<char[], Character> {+        private char[] arr = new char[0];+        public int getSize() { return arr.length; };+        public Character get(int i) { return arr[i]; };+        public void setBatch(char[] b) { arr = b; };+        public Class<Character> getElemClass() { return Character.class; };+    }++    public static final class ShortBatchReader implements BatchReader<short[], Short> {+        private short[] arr = new short[0];+        public int getSize() { return arr.length; };+        public Short get(int i) { return arr[i]; };+        public void setBatch(short[] b) { arr = b; };+        public Class<Short> getElemClass() { return Short.class; };+    }++    public static final class IntegerBatchReader implements BatchReader<int[], Integer> {+        private int[] arr = new int[0];+        public int getSize() { return arr.length; };+        public Integer get(int i) { return arr[i]; };+        public void setBatch(int[] b) { arr = b; };+        public Class<Integer> getElemClass() { return Integer.class; };+    }++    public static final class LongBatchReader implements BatchReader<long[], Long> {+        private long[] arr = new long[0];+        public int getSize() { return arr.length; };+        public Long get(int i) { return arr[i]; };+        public void setBatch(long[] b) { arr = b; };+        public Class<Long> getElemClass() { return Long.class; };+    }++    public static final class FloatBatchReader implements BatchReader<float[], Float> {+        private float[] arr = new float[0];+        public int getSize() { return arr.length; };+        public Float get(int i) { return arr[i]; };+        public void setBatch(float[] b) { arr = b; };+        public Class<Float> getElemClass() { return Float.class; };+    }++    public static final class DoubleBatchReader implements BatchReader<double[], Double> {+        private double[] arr = new double[0];+        public int getSize() { return arr.length; };+        public Double get(int i) { return arr[i]; };+        public void setBatch(double[] b) { arr = b; };+        public Class<Double> getElemClass() { return Double.class; };+    }++    public static final class ObjectBatchReader implements BatchReader<Object[], Object> {+        private Object[] arr = new Object[0];+        public int getSize() { return arr.length; };+        public Object get(int i) { return arr[i]; };+        public void setBatch(Object[] b) { arr = b; };+        public Class<Object> getElemClass() { return Object.class; };+    }++    public static final class ByteArrayBatchReader+            implements BatchReader<Tuple2<byte[], int[]>, byte[]> {+        private byte[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<byte[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public byte[] get(int i) {+            final int len = end[i] - start;+            byte[] res = new byte[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<byte[]> getElemClass() { return byte[].class; };+    }++    public static final class BooleanArrayBatchReader+            implements BatchReader<Tuple2<boolean[], int[]>, boolean[]> {+        private boolean[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<boolean[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public boolean[] get(int i) {+            final int len = end[i] - start;+            boolean[] res = new boolean[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<boolean[]> getElemClass() { return boolean[].class; };+    }++    public static final class CharArrayBatchReader+            implements BatchReader<Tuple2<char[], int[]>, char[]> {+        private char[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<char[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public char[] get(int i) {+            final int len = end[i] - start;+            char[] res = new char[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<char[]> getElemClass() { return char[].class; };+    }++    public static final class ShortArrayBatchReader+            implements BatchReader<Tuple2<short[], int[]>, short[]> {+        private short[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<short[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public short[] get(int i) {+            final int len = end[i] - start;+            short[] res = new short[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<short[]> getElemClass() { return short[].class; };+    }++    public static final class IntArrayBatchReader+            implements BatchReader<Tuple2<int[], int[]>, int[]> {+        private int[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<int[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public int[] get(int i) {+            final int len = end[i] - start;+            int[] res = new int[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<int[]> getElemClass() { return int[].class; };+    }++    public static final class LongArrayBatchReader+            implements BatchReader<Tuple2<long[], int[]>, long[]> {+        private long[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<long[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public long[] get(int i) {+            final int len = end[i] - start;+            long[] res = new long[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<long[]> getElemClass() { return long[].class; };+    }++    public static final class FloatArrayBatchReader+            implements BatchReader<Tuple2<float[], int[]>, float[]> {+        private float[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<float[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public float[] get(int i) {+            final int len = end[i] - start;+            float[] res = new float[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<float[]> getElemClass() { return float[].class; };+    }++    public static final class DoubleArrayBatchReader+            implements BatchReader<Tuple2<double[], int[]>, double[]> {+        private double[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<double[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public double[] get(int i) {+            final int len = end[i] - start;+            double[] res = new double[len];+            System.arraycopy(arr, start, res, 0, len);+            start = end[i];+            return res;+        }+        public Class<double[]> getElemClass() { return double[].class; };+    }++    public static final class ObjectArrayBatchReader<A, T>+            implements BatchReader<Tuple2<A, int[]>, T[]> {+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        private final BatchReader<A, T> br;+        public ObjectArrayBatchReader(BatchReader<A, T> br) {+            this.br = br;+        }+        public void setBatch(Tuple2<A, int[]> t) {+            br.setBatch(t._1);+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public T[] get(int i) {+            final int len = end[i] - start;+            T[] res = (T[]) java.lang.reflect.Array.newInstance(br.getElemClass(), len);+            for(int j=0;j<len;j++)+                res[j] = br.get(start + j);+            start = end[i];+            return res;+        }+        public Class<T[]> getElemClass() {+            return (Class<T[]>) java.lang.reflect.Array.newInstance(br.getElemClass(), 0).getClass();+        }+    }++    public static final class StringArrayBatchReader+            implements BatchReader<Tuple2<char[], int[]>, String> {+        private char[] arr;+        // end[i] tells the first position after the i-th array.+        private int[] end = new int[0];+        private int start;+        public void setBatch(Tuple2<char[], int[]> t) {+            arr = t._1;+            end = t._2;+            start = 0;+        }+        public int getSize() { return end.length; }+        public String get(int i) {+            final int len = end[i] - start;+            String res = new String(arr, start, len);+            start = end[i];+            return res;+        }+        public Class<String> getElemClass() { return String.class; };+    }+}
+ src/main/java/io/tweag/jvm/batching/BatchWriter.java view
@@ -0,0 +1,14 @@+package io.tweag.jvm.batching;++/**+ *  A BatchWriter<A,B> is an object that takes elements of type A one at+ *  a time and yields a batch of type B containing all of them.+ */+public interface BatchWriter<A, B> {+    /// Starts a new batch of the given size+    public void start(int batchSize);+    /// Sets the object at position i of the batch.+    public void set(int i, A o);+    /// Yields the batch.+    public B getBatch();+}
+ src/main/java/io/tweag/jvm/batching/BatchWriters.java view
@@ -0,0 +1,409 @@+package io.tweag.jvm.batching;++/**+ *  Various batchers+ *+ * */+public final class BatchWriters {++    public static final class BooleanBatchWriter implements BatchWriter<Boolean, boolean[]> {+        private boolean[] arr;+        public void start(int batchSize) { arr = new boolean[batchSize]; };+        public void set(int i, Boolean o) { arr[i] = o; };+        public boolean[] getBatch() { return arr; };+    }++    public static final class CharacterBatchWriter implements BatchWriter<Character, char[]> {+        private char[] arr;+        public void start(int batchSize) { arr = new char[batchSize]; };+        public void set(int i, Character o) { arr[i] = o; };+        public char[] getBatch() { return arr; };+    }++    public static final class ByteBatchWriter implements BatchWriter<Byte, byte[]> {+        private byte[] arr;+        public void start(int batchSize) { arr = new byte[batchSize]; };+        public void set(int i, Byte o) { arr[i] = o; };+        public byte[] getBatch() { return arr; };+    }++    public static final class ShortBatchWriter implements BatchWriter<Short, short[]> {+        private short[] arr;+        public void start(int batchSize) { arr = new short[batchSize]; };+        public void set(int i, Short o) { arr[i] = o; };+        public short[] getBatch() { return arr; };+    }++    public static final class IntegerBatchWriter implements BatchWriter<Integer, int[]> {+        private int[] arr;+        public void start(int batchSize) { arr = new int[batchSize]; };+        public void set(int i, Integer o) { arr[i] = o; };+        public int[] getBatch() { return arr; };+    }++    public static final class LongBatchWriter implements BatchWriter<Long, long[]> {+        private long[] arr;+        public void start(int batchSize) { arr = new long[batchSize]; };+        public void set(int i, Long o) { arr[i] = o; };+        public long[] getBatch() { return arr; };+    }++    public static final class FloatBatchWriter implements BatchWriter<Float, float[]> {+        private float[] arr;+        public void start(int batchSize) { arr = new float[batchSize]; };+        public void set(int i, Float o) { arr[i] = o; };+        public float[] getBatch() { return arr; };+    }++    public static final class DoubleBatchWriter implements BatchWriter<Double, double[]> {+        private double[] arr;+        public void start(int batchSize) { arr = new double[batchSize]; };+        public void set(int i, Double o) { arr[i] = o; };+        public double[] getBatch() { return arr; };+    }++    public static final class ObjectBatchWriter implements BatchWriter<Object, Object[]> {+        private Object[] arr;+        public void start(int batchSize) { arr = new Object[batchSize]; };+        public void set(int i, Object o) { arr[i] = o; };+        public Object[] getBatch() { return arr; };+    }++    public static final class ByteArrayBatchWriter+            implements BatchWriter<byte[], Tuple2<byte[], int[]> > {+        private byte[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new byte[size][];+            top = 0;+        }+        public void set(int i, byte[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<byte[], int[]> getBatch() {+            byte[] batch = new byte[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<byte[], int[]>(batch, end);+        }+    }++    public static final class BooleanArrayBatchWriter+            implements BatchWriter<boolean[], Tuple2<boolean[], int[]> > {+        private boolean[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new boolean[size][];+            top = 0;+        }+        public void set(int i, boolean[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<boolean[], int[]> getBatch() {+            boolean[] batch = new boolean[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<boolean[], int[]>(batch, end);+        }+    }++    public static final class CharArrayBatchWriter+            implements BatchWriter<char[], Tuple2<char[], int[]> > {+        private char[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new char[size][];+            top = 0;+        }+        public void set(int i, char[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<char[], int[]> getBatch() {+            char[] batch = new char[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<char[], int[]>(batch, end);+        }+    }++    public static final class ShortArrayBatchWriter+            implements BatchWriter<short[], Tuple2<short[], int[]> > {+        private short[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new short[size][];+            top = 0;+        }+        public void set(int i, short[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<short[], int[]> getBatch() {+            short[] batch = new short[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<short[], int[]>(batch, end);+        }+    }++    public static final class IntArrayBatchWriter+            implements BatchWriter<int[], Tuple2<int[], int[]> > {+        private int[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new int[size][];+            top = 0;+        }+        public void set(int i, int[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<int[], int[]> getBatch() {+            int[] batch = new int[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<int[], int[]>(batch, end);+        }+    }++    public static final class LongArrayBatchWriter+            implements BatchWriter<long[], Tuple2<long[], int[]> > {+        private long[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new long[size][];+            top = 0;+        }+        public void set(int i, long[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<long[], int[]> getBatch() {+            long[] batch = new long[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<long[], int[]>(batch, end);+        }+    }++    public static final class FloatArrayBatchWriter+            implements BatchWriter<float[], Tuple2<float[], int[]> > {+        private float[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new float[size][];+            top = 0;+        }+        public void set(int i, float[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<float[], int[]> getBatch() {+            float[] batch = new float[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<float[], int[]>(batch, end);+        }+    }++    public static final class DoubleArrayBatchWriter+            implements BatchWriter<double[], Tuple2<double[], int[]> > {+        private double[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new double[size][];+            top = 0;+        }+        public void set(int i, double[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<double[], int[]> getBatch() {+            double[] batch = new double[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<double[], int[]>(batch, end);+        }+    }++    public static final class ObjectArrayBatchWriter<T, B>+            implements BatchWriter<T[], Tuple2<B, int[]> > {+        private final BatchWriter<T, B> ob;+        private T[][] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;++        public ObjectArrayBatchWriter(BatchWriter<T, B> ob) {+            this.ob = ob;+        }++        public void start(int size) {+            end = new int[size];+            arrays = (T[][]) new Object[size][];+            top = 0;+        }+        public void set(int i, T[] vec) {+            if (vec == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = vec;+            top += vec.length;+            end[i] = top;+        }+        public Tuple2<B, int[]> getBatch() {+            ob.start(top);+            int k = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                for(int j=0;j<arrays[i].length;j++) {+                    ob.set(k, arrays[i][j]);+                    k++;+                }+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<B, int[]>(ob.getBatch(), end);+        }+    }++    public static final class StringArrayBatchWriter+            implements BatchWriter<String, Tuple2<char[], int[]> > {+        private String[] arrays;+        // end[i] tells the first position after the i-th array.+        private int[] end;+        // top tells the first position not occupied by arrays in the batch.+        private int top;+        public void start(int size) {+            end = new int[size];+            arrays = new String[size];+            top = 0;+        }+        public void set(int i, String s) {+            if (s == null)+                throw new RuntimeException("null vectors are unsupported when reifying");+            arrays[i] = s;+            top += s.length();+            end[i] = top;+        }+        public Tuple2<char[], int[]> getBatch() {+            char[] batch = new char[top];+            int pos = 0;+            for(int i=0;i<end.length && arrays[i]!=null;i++) {+                arrays[i].getChars(0, arrays[i].length(), batch, pos);+                pos = end[i];+                // Release the reference to the array so it can be reclaimed+                // before the loop is over.+                arrays[i] = null;+            }+            return new Tuple2<char[], int[]>(batch, end);+        }+    }+}
+ src/main/java/io/tweag/jvm/batching/Tuple2.java view
@@ -0,0 +1,10 @@+package io.tweag.jvm.batching;++public class Tuple2<A, B> {+    public A _1;+    public B _2;+    public Tuple2(A _1, B _2) {+        this._1 = _1;+        this._2 = _2;+    }+}
+ src/test/haskell/Language/Java/BatchingSpec.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}++module Language.Java.BatchingSpec where++import qualified Data.ByteString as BS+import Data.Int+import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Vector as V+import qualified Data.Vector.Storable as VS+import Language.Java+import Language.Java.Batching ()+import Test.Hspec++spec :: Spec+spec = do+    let testrr :: (Eq a, Show a, Reify a, Reflect a) => a -> IO ()+        testrr x = (reflect x >>= reify) `shouldReturn` x+    describe "batching" $ do+      it "succeeds on empty vectors" $ do+        testrr (V.fromList [] :: V.Vector Text)+      it "succeeds on non-empty vectors" $ do+        testrr (V.fromList [1..10] :: V.Vector Int32)+      it "succeeds on vectors of vectors" $ do+        let xs :: V.Vector (V.Vector Int32)+            xs = V.fromList (map V.fromList [ [i .. i + 4] | i <- [1,6..26] ])+        testrr xs+        testrr (V.empty `asTypeOf` xs)+        testrr $ V.map (V.map (Text.pack . show)) xs+        testrr $ V.map (V.map (BS.singleton . fromIntegral)) xs+        testrr (V.map V.convert xs :: V.Vector (VS.Vector Int32))
+ src/test/haskell/Main.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Language.Java (withJVM)+import qualified Spec+import Test.Hspec++main :: IO ()+main = withJVM ["-Djava.class.path=build/libs/jvm-batching.jar"] $+    hspec Spec.spec
+ src/test/haskell/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}