Foreign.Storable — 원시 메모리 마샬링 클래스
Foreign.Storable — 원시 메모리 마샬링 클래스
Foreign.Storable은 기본 타입 값을 원시 메모리(생 메모리)에 쓰고 읽는 클래스예요. FFI 코드에서는 Haskell 데이터 구조를 외부 언어의 이진 표현으로 옮기고 다시 되돌리는 일이 결국 필요한데, 이 클래스는 그런 마샬링을 표준 기본 타입을 비롯한 여러 타입에 대해 제공해요. 이 모듈은 값의 저장 크기와 정렬 제약을 계산하는 기능, 메모리 위치에서 값을 읽고 쓰는 기능을 함께 담고 있어요.
본문
이 모듈이 내보내는 것들은 다음과 같아요.
module Foreign.Storable (
Storable(sizeOf,
alignment,
peekElemOff,
pokeElemOff,
peekByteOff,
pokeByteOff,
peek,
poke)
) where
class Storable a where
이 클래스의 멤버 함수들은 기본 타입의 값을 원시 메모리(위에서 언급한 루틴들로 할당되었을 수 있는 메모리)에 쓰고, 원시 메모리 블록에서 값을 읽는 일을 돕는 기능들이에요. 또한 이 클래스는 Storable 타입들의 저장 공간 요구량과 정렬 제약을 계산하는 지원도 포함해요. 메모리 주소는 Storable 클래스의 인스턴스인 어떤 a에 대해 Ptr a 타입의 값으로 표현돼요. Ptr의 타입 인자는 FFI 코드에서 값진 타입 안전성을 제공해요(명시적 캐스팅 없이는 서로 다른 타입의 포인터를 섞을 수 없어요). 그러면서도 주어진 포인터에 어떤 마샬링 메서드가 필요한지 Haskell 타입 시스템이 알아내도록 도와줘요. Haskell과 외부 언어 사이의 모든 마샬링은 궁극적으로 Haskell 데이터 구조를 외부 언어의 대응 데이터 구조의 이진 표현으로 옮기고, 그 반대 방향으로도 옮기는 일로 요약돼요. 이러한 마샬링을 Haskell로 코딩하려면, 구조화되지 않은 메모리 블록에 저장된 기본 타입 데이터를 조작할 필요가 있어요. Storable 클래스는 그것이 인스턴스화되는 모든 타입에서 이러한 조작을 가능하게 해주는데, 그 타입들은 Haskell의 표준 기본 타입들, 고정 크기 Int 타입들(Int8, Int16, Int32, Int64), 고정 크기 Word 타입들(Word8, Word16, Word32, Word64), StablePtr, Foreign.C.Types의 모든 타입, 그리고 Ptr이에요. 최소 완전 정의(minimal complete definition): sizeOf, alignment, 그리고 peek, peekElemOff, peekByteOff 중 하나, 그리고 poke, pokeElemOff, pokeByteOff 중 하나예요. 메서드는 다음과 같아요.
sizeOf :: a -> Int
인자의 저장 공간 요구량(바이트 단위)을 계산해요. 인자의 값 자체는 사용되지 않아요.
alignment :: a -> Int
인자의 정렬 제약을 계산해요. 정렬 제약 x는 x로 나누어떨어지는 모든 주소에 의해 충족돼요. 인자의 값 자체는 사용되지 않아요.
peekElemOff :: Ptr a -> Int -> IO a
같은 종류의 값 배열로 간주되는 메모리 영역에서 값을 읽어요. 첫 번째 인자는 배열의 시작 주소를, 두 번째 인자는 배열 안의 인덱스(배열의 첫 원소는 인덱스 0)를 지정해요. 다음의 등식이 성립해요.
peekElemOff addr idx = IOExts.fixIO $ \result ->
peek (addr ‘plusPtr‘ (idx ⋆ sizeOf result))
이것은 명세(specification)일 뿐, 함수의 실제 구현은 아니라는 점을 기억해요.
pokeElemOff :: Ptr a -> Int -> a -> IO ()
같은 종류의 값 배열로 간주되는 메모리 영역에 값을 써요. 다음의 등식이 성립해요.
pokeElemOff addr idx x =
poke (addr ‘plusPtr‘ (idx ⋆ sizeOf x)) x
peekByteOff :: Ptr b -> Int -> IO a
기본 주소와 오프셋으로 주어진 메모리 위치에서 값을 읽어요. 다음의 등식이 성립해요.
peekByteOff addr off = peek (addr ‘plusPtr‘ off)
pokeByteOff :: Ptr b -> Int -> a -> IO ()
기본 주소와 오프셋으로 주어진 메모리 위치에 값을 써요. 다음의 등식이 성립해요.
pokeByteOff addr off x = poke (addr ‘plusPtr‘ off) x
peek :: Ptr a -> IO a
주어진 메모리 위치에서 값을 읽어요. peek과 poke 함수는 올바르게 동작하려면 제대로 정렬된 주소가 필요할 수 있음을 기억해요. 이는 아키텍처에 따라 달라지므로, 이식 가능한 코드라면 어떤 타입 a의 값을 peek/poke할 때 alignment 함수가 주는 a에 대한 정렬 제약이 충족되는지 확인해야 해요.
poke :: Ptr a -> a -> IO ()
주어진 값을 주어진 메모리 위치에 써요. 정렬 제약이 적용될 수 있어요. peek을 참고해요.
instance Storable Bool instance Storable Char instance Storable Double instance Storable Float instance Storable Int instance Storable Int8 instance Storable Int16 instance Storable Int32 instance Storable Int64 instance Storable Word instance Storable Word8 instance Storable Word16 instance Storable Word32 instance Storable Word64 instance Storable WordPtr instance Storable IntPtr instance Storable CChar instance Storable CSChar instance Storable CUChar instance Storable CShort instance Storable CUShort instance Storable CInt instance Storable CUInt instance Storable CLong instance Storable CULong instance Storable CLLong instance Storable CULLong instance Storable CFloat instance Storable CDouble instance Storable CPtrdiff instance Storable CSize instance Storable CWchar instance Storable CSigAtomic instance Storable CClock instance Storable CTime instance Storable CIntPtr instance Storable CUIntPtr instance Storable CIntMax instance Storable CUIntMax instance Storable (StablePtr a) instance Storable (Ptr a) instance Storable (FunPtr a)