mirror of
https://github.com/zap-proto/haskell.git
synced 2026-10-30 15:05:00 +00:00
Mostly optimizing Message.write. This started off as an attempt to remove some of the cruft in the allocation routines, get rid of the now-superfluous AppendVec for segments, and remove the indirection of the MutVar for access to the segment data (since we never actually resize the segments anymore). In the course of doing this I discovered we actually *do* still sometimes resize segments, specifically in the case where a double-far pointer could be used. This was probably bad for performance, though I'm not sure how often it came up in practice. This patch gives us another ~20% speedup for ocap-merkledag.
167 lines
5.7 KiB
Haskell
167 lines
5.7 KiB
Haskell
{- |
|
|
Module: Capnp.Pointer
|
|
Description: Support for parsing/serializing capnproto pointers
|
|
|
|
This module provides support for parsing and serializing capnproto pointers.
|
|
This is a low-level module; most users will not need to call it directly.
|
|
-}
|
|
module Capnp.Pointer
|
|
( Ptr(..)
|
|
, ElementSize(..)
|
|
, EltSpec(..)
|
|
, parsePtr
|
|
, parsePtr'
|
|
, serializePtr
|
|
, serializePtr'
|
|
, parseEltSpec
|
|
, serializeEltSpec
|
|
)
|
|
where
|
|
|
|
import Data.Bits
|
|
import Data.Int
|
|
import Data.Word
|
|
|
|
import Capnp.Bits
|
|
|
|
-- | A 'Ptr' represents the information in a capnproto pointer.
|
|
data Ptr
|
|
= StructPtr !Int32 !Word16 !Word16
|
|
-- ^ @'StructPtr' off dataSz ptrSz@ is a pointer to a struct
|
|
-- at offset @off@ in words from the end of the pointer, with
|
|
-- a data section of size @dataSz@ words, and a pointer section
|
|
-- of size @ptrSz@ words.
|
|
--
|
|
-- Note that the value @'StructPtr' 0 0 0@ is illegal, since
|
|
-- its encoding is reserved for the "null" pointer.
|
|
| ListPtr !Int32 !EltSpec
|
|
-- ^ @'ListPtr' off eltSpec@ is a pointer to a list starting at
|
|
-- offset @off@ in words from the end of the pointer. @eltSpec@
|
|
-- encodes the C and D fields in the encoding spec; see 'EltSpec'
|
|
-- for details
|
|
| FarPtr !Bool !Word32 !Word32
|
|
-- ^ @'FarPtr' twoWords off segment@ is a far pointer, whose landing
|
|
-- pad is:
|
|
--
|
|
-- * two words iff @twoWords@,
|
|
-- * @off@ words from the start of the target segment, and
|
|
-- * in segment id @segment@.
|
|
| CapPtr !Word32
|
|
-- ^ @'CapPtr' id@ is a pointer to the capability with the id @id@.
|
|
deriving(Show, Eq)
|
|
|
|
|
|
-- | The element size field in a list pointer.
|
|
data ElementSize
|
|
= Sz0
|
|
| Sz1
|
|
| Sz8
|
|
| Sz16
|
|
| Sz32
|
|
| Sz64
|
|
| SzPtr
|
|
deriving(Show, Eq, Enum)
|
|
|
|
-- | A combination of the C and D fields in a list pointer, i.e. the element
|
|
-- size, and either the number of elements in the list, or the total number
|
|
-- of /words/ in the list (if size is composite).
|
|
data EltSpec
|
|
= EltNormal !ElementSize !Word32
|
|
-- ^ @'EltNormal' size len@ is a normal (non-composite) element type
|
|
-- (C /= 7). @size@ is the size of the elements, and @len@ is the
|
|
-- number of elements in the list.
|
|
| EltComposite !Int32
|
|
-- ^ @EltComposite len@ is a composite element (C == 7). @len@ is the
|
|
-- length of the list in words.
|
|
deriving(Show, Eq)
|
|
|
|
-- | @'parsePtr' word@ parses word as a capnproto pointer. A null pointer is
|
|
-- parsed as 'Nothing'.
|
|
parsePtr :: Word64 -> Maybe Ptr
|
|
parsePtr 0 = Nothing
|
|
parsePtr p = Just (parsePtr' p)
|
|
|
|
-- | @'parsePtr'' word@ parses @word@ as a capnproto pointer. It ignores
|
|
-- nulls, returning them the same as @(StructPtr 0 0 0)@.
|
|
parsePtr' :: Word64 -> Ptr
|
|
parsePtr' word =
|
|
case bitRange word 0 2 :: Word64 of
|
|
0 -> StructPtr
|
|
(i30 (lo word))
|
|
(bitRange word 32 48)
|
|
(bitRange word 48 64)
|
|
1 -> ListPtr
|
|
(i30 (lo word))
|
|
(parseEltSpec word)
|
|
2 -> FarPtr
|
|
(toEnum (bitRange word 2 3))
|
|
(bitRange word 3 32)
|
|
(bitRange word 32 64)
|
|
3 -> CapPtr (bitRange word 32 64)
|
|
_ -> error "unreachable"
|
|
|
|
-- | @'serializePtr' ptr@ serializes the pointer as a 'Word64', translating
|
|
-- 'Nothing' to a null pointer.
|
|
--
|
|
-- This also changes the offset of zero-sized struct pointers to -1, to avoid
|
|
-- them being interpreted as null.
|
|
serializePtr :: Maybe Ptr -> Word64
|
|
serializePtr Nothing = 0
|
|
serializePtr (Just p@(StructPtr (-1) 0 0)) =
|
|
serializePtr' p
|
|
serializePtr (Just (StructPtr _ 0 0)) =
|
|
-- We need to handle this specially, for two reasons.
|
|
--
|
|
-- First, if the offset is zero, the the normal encoding would be interpreted
|
|
-- as null. We can get around this by changing the offset to -1, which will
|
|
-- point immediately before the pointer, which is always a valid position --
|
|
-- and since the size is zero, we can stick it at any valid position.
|
|
--
|
|
-- Second, the canonicalization algorithm requires that *all* zero size structs
|
|
-- are encoded this way, and doing this for all offsets, rather than only zero
|
|
-- offsets, avoids needing extra logic elsewhere.
|
|
serializePtr' (StructPtr (-1) 0 0)
|
|
serializePtr (Just p) =
|
|
serializePtr' p
|
|
|
|
-- | @'serializePtr'' ptr@ serializes the pointer as a Word64.
|
|
--
|
|
-- Unlike 'serializePtr', this results in a null pointer on the input
|
|
-- @(StructPtr 0 0 0)@, rather than adjusting the offset.
|
|
serializePtr' :: Ptr -> Word64
|
|
serializePtr' (StructPtr off dataSz ptrSz) =
|
|
-- 0 .|.
|
|
fromLo (fromI30 off) .|.
|
|
(fromIntegral dataSz `shiftL` 32) .|.
|
|
(fromIntegral ptrSz `shiftL` 48)
|
|
serializePtr' (ListPtr off eltSpec) = -- eltSz numElts) =
|
|
1 .|.
|
|
fromLo (fromI30 off) .|.
|
|
serializeEltSpec eltSpec
|
|
serializePtr' (FarPtr twoWords off segId) =
|
|
2 .|.
|
|
(fromIntegral (fromEnum twoWords) `shiftL` 2) .|.
|
|
(fromIntegral off `shiftL` 3) .|.
|
|
(fromIntegral segId `shiftL` 32)
|
|
serializePtr' (CapPtr index) =
|
|
3 .|.
|
|
-- (fromIntegral 0 `shiftL` 2) .|.
|
|
(fromIntegral index `shiftL` 32)
|
|
|
|
-- | @'parseEltSpec' word@ reads the 'EltSpec' from @word@, which must be the
|
|
-- encoding of a list pointer (this is not verified).
|
|
parseEltSpec :: Word64 -> EltSpec
|
|
parseEltSpec word = case bitRange word 32 35 of
|
|
7 -> EltComposite (i29 (hi word))
|
|
sz -> EltNormal (toEnum sz) (bitRange word 35 64)
|
|
|
|
-- | @'serializeEltSpec' eltSpec@ serializes @eltSpec@ as a 'Word64'. all bits
|
|
-- which are not determined by the 'EltSpec' are zero.
|
|
serializeEltSpec :: EltSpec -> Word64
|
|
serializeEltSpec (EltNormal sz len) =
|
|
(fromIntegral (fromEnum sz) `shiftL` 32) .|.
|
|
(fromIntegral len `shiftL` 35)
|
|
serializeEltSpec (EltComposite words) =
|
|
(7 `shiftL` 32) .|.
|
|
fromHi (fromI29 words)
|