diff --git a/Data/Text/ICU/Convert.hs b/Data/Text/ICU/Convert.hs
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Convert.hs
@@ -0,0 +1,236 @@
+{-# LANGUAGE CPP, ForeignFunctionInterface #-}
+-- |
+-- Module      : Data.Text.ICU.Convert
+-- Copyright   : (c) Bryan O'Sullivan 2009
+--
+-- License     : BSD-style
+-- Maintainer  : bos@serpentine.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Character set conversion functions for Unicode, implemented as
+-- bindings to the International Components for Unicode (ICU)
+-- libraries.
+
+module Data.Text.ICU.Convert
+    (
+    -- * Character set conversion
+      Converter
+    -- ** Basic functions
+    , open
+    , fromUnicode
+    , toUnicode
+    -- ** Converter metadata
+    , getName
+    , usesFallback
+    , isAmbiguous
+    -- * Functions for controlling global behavior
+    , getDefaultName
+    , setDefaultName
+    -- * Miscellaneous functions
+    , compareNames
+    , aliases
+    -- * Metadata
+    , converterNames
+    , standardNames
+    ) where
+
+import Data.ByteString.Internal (ByteString, createAndTrim)
+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
+import Data.Int (Int32)
+import Data.Text (Text)
+import Data.Text.Foreign (fromPtr, lengthWord16, useAsPtr)
+import Data.Text.ICU.Convert.Internal
+import Data.Text.ICU.Error.Internal (UErrorCode, handleError)
+import Data.Word (Word16)
+import Foreign.C.String (CString, peekCString, withCString)
+import Foreign.C.Types (CInt)
+import Foreign.ForeignPtr (newForeignPtr)
+import Foreign.Marshal.Array (allocaArray)
+import Foreign.Ptr (FunPtr, Ptr, castPtr, nullPtr)
+import System.IO.Unsafe (unsafePerformIO)
+import Data.Text.ICU.Internal (UBool, UChar, asBool, asOrdering)
+
+-- | Do a fuzzy compare of two converter/alias names.  The comparison
+-- is case-insensitive, ignores leading zeroes if they are not
+-- followed by further digits, and ignores all but letters and digits.
+-- Thus the strings @\"UTF-8\"@, @\"utf_8\"@, @\"u*T\@f08\"@ and
+-- @\"Utf 8\"@ are exactly equivalent.  See section 1.4, Charset Alias
+-- Matching in Unicode Technical Standard #22 at
+-- <http://www.unicode.org/reports/tr22/>
+compareNames :: String -> String -> Ordering
+compareNames a b =
+  unsafePerformIO . withCString a $ \aptr ->
+    fmap asOrdering . withCString b $ ucnv_compareNames aptr
+
+-- | Create a 'Converter' with the name of a coded character set
+-- specified as a string.  The actual name will be resolved with the
+-- alias file using a case-insensitive string comparison that ignores
+-- leading zeroes and all non-alphanumeric characters.  E.g., the
+-- names @\"UTF8\"@, @\"utf-8\"@, @\"u*T\@f08\"@ and @\"Utf 8\"@ are
+-- all equivalent (see also 'compareNames').  If an empty string is
+-- passed for the converter name, it will create one with the
+-- 'getDefaultName' return value.
+--
+-- A converter name may contain options like a locale specification to
+-- control the specific behavior of the newly instantiated converter.
+-- The meaning of the options depends on the particular converter.  If
+-- an option is not defined for or recognized by a given converter,
+-- then it is ignored.
+--
+-- Options are appended to the converter name string, with a comma
+-- between the name and the first option and also between adjacent
+-- options.
+--
+-- If the alias is ambiguous, then the preferred converter is used.
+--
+-- The conversion behavior and names can vary between platforms. ICU
+-- may convert some characters differently from other
+-- platforms. Details on this topic are in the ICU User's Guide at
+-- <http://icu-project.org/userguide/conversion.html>. Aliases
+-- starting with a @\"cp\"@ prefix have no specific meaning other than
+-- its an alias starting with the letters @\"cp\"@. Please do not
+-- associate any meaning to these aliases.
+open :: String                  -- ^ Name of the converter to use.
+     -> Maybe Bool              -- ^ Whether to use fallback mappings
+                                -- (see 'usesFallback' for details).
+     -> IO Converter
+open name mf = do
+  c <- fmap Converter . newForeignPtr ucnv_close =<< named (handleError . ucnv_open)
+  case mf of
+    Just f -> withConverter c $ \p -> ucnv_setFallback p . fromIntegral . fromEnum $ f
+    _ -> return ()
+  return c
+  where named act
+            | null name = act nullPtr
+            | otherwise = withCString name act
+
+-- | Convert the Unicode string into a codepage string using the given
+-- converter.
+fromUnicode :: Converter -> Text -> ByteString
+fromUnicode cnv t =
+  unsafePerformIO . useAsPtr t $ \tptr tlen ->
+    withConverter cnv $ \cptr -> do
+      let capacity = fromIntegral . max_bytes_for_string cptr . fromIntegral $
+                     lengthWord16 t
+      createAndTrim (fromIntegral capacity) $ \sptr ->
+        fmap fromIntegral . handleError $
+           ucnv_fromUChars cptr (castPtr sptr) capacity tptr (fromIntegral tlen)
+
+-- | Convert the codepage string into a Unicode string using the given
+-- converter.
+toUnicode :: Converter -> ByteString -> Text
+toUnicode cnv bs =
+  unsafePerformIO . unsafeUseAsCStringLen bs $ \(sptr, slen) ->
+    withConverter cnv $ \cptr -> do
+      let capacity = slen * 2
+      allocaArray capacity $ \tptr -> 
+        fromPtr tptr =<< (fmap fromIntegral . handleError $
+                          ucnv_toUChars cptr tptr (fromIntegral capacity) sptr
+                                        (fromIntegral slen))
+
+-- | Determines whether the converter uses fallback mappings or not.
+-- This flag has restrictions.  Regardless of this flag, the converter
+-- will always use fallbacks from Unicode Private Use code points, as
+-- well as reverse fallbacks (to Unicode).  For details see \".ucm
+-- File Format\" in the Conversion Data chapter of the ICU User Guide:
+-- <http://www.icu-project.org/userguide/conversion-data.html#ucmformat>
+usesFallback :: Converter -> Bool
+usesFallback cnv = unsafePerformIO $
+                   asBool `fmap` withConverter cnv ucnv_usesFallback
+
+-- | Returns the current default converter name. If you want to 'open'
+-- a default converter, you do not need to use this function.  It is
+-- faster to pass the empty string to 'open' the default converter.
+getDefaultName :: IO String
+getDefaultName = peekCString =<< ucnv_getDefaultName
+
+-- | Indicates whether the converter contains ambiguous mappings of
+-- the same character or not.
+isAmbiguous :: Converter -> Bool
+isAmbiguous cnv = asBool . unsafePerformIO $ withConverter cnv ucnv_isAmbiguous
+
+-- | Sets the current default converter name. If this function needs
+-- to be called, it should be called during application
+-- initialization. Most of the time, the results from 'getDefaultName'
+-- or 'open' with an empty string argument is sufficient for your
+-- application.
+--
+-- /Note/: this function is not thread safe. /Do not/ call this
+-- function when /any/ ICU function is being used from more than one
+-- thread!
+setDefaultName :: String -> IO ()
+setDefaultName s = withCString s $ ucnv_setDefaultName
+
+-- | A list of the canonical names of all available converters.
+converterNames :: [String]
+{-# NOINLINE converterNames #-}
+converterNames = unsafePerformIO $
+  mapM ((peekCString =<<) . ucnv_getAvailableName) [0..ucnv_countAvailable-1]
+
+-- | The list of supported standard names.
+standardNames :: [String]
+{-# NOINLINE standardNames #-}
+standardNames = filter (not . null) . unsafePerformIO $
+  mapM ((peekCString =<<) . handleError . ucnv_getStandard) [0..ucnv_countStandards-1]
+
+-- | Return the aliases for a given converter or alias name.
+aliases :: String -> [String]
+aliases name = unsafePerformIO . withCString name $ \ptr -> do
+  count <- handleError $ ucnv_countAliases ptr
+  if count == 0
+    then return []
+    else mapM ((peekCString =<<) . handleError . ucnv_getAlias ptr) [0..count-1]
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_open" ucnv_open
+    :: CString -> Ptr UErrorCode -> IO (Ptr UConverter)
+
+foreign import ccall unsafe "hs_text_icu.h &__hs_ucnv_close" ucnv_close
+    :: FunPtr (Ptr UConverter -> IO ())
+
+foreign import ccall unsafe "__get_max_bytes_for_string" max_bytes_for_string
+    :: Ptr UConverter -> CInt -> CInt
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_toUChars" ucnv_toUChars
+    :: Ptr UConverter -> Ptr UChar -> Int32 -> CString -> Int32
+    -> Ptr UErrorCode -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_fromUChars" ucnv_fromUChars
+    :: Ptr UConverter -> CString -> Int32 -> Ptr UChar -> Int32
+    -> Ptr UErrorCode -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_compareNames" ucnv_compareNames
+    :: CString -> CString -> IO CInt
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getDefaultName" ucnv_getDefaultName
+    :: IO CString
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_setDefaultName" ucnv_setDefaultName
+    :: CString -> IO ()
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_countAvailable" ucnv_countAvailable
+    :: Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getAvailableName" ucnv_getAvailableName
+    :: Int32 -> IO CString
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_countAliases" ucnv_countAliases
+    :: CString -> Ptr UErrorCode -> IO Word16
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getAlias" ucnv_getAlias
+    :: CString -> Word16 -> Ptr UErrorCode -> IO CString
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_countStandards" ucnv_countStandards
+    :: Word16
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getStandard" ucnv_getStandard
+    :: Word16 -> Ptr UErrorCode -> IO CString
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_usesFallback" ucnv_usesFallback
+    :: Ptr UConverter -> IO UBool
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_setFallback" ucnv_setFallback
+    :: Ptr UConverter -> UBool -> IO ()
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_isAmbiguous" ucnv_isAmbiguous
+    :: Ptr UConverter -> IO UBool
diff --git a/Data/Text/ICU/Convert/Internal.hs b/Data/Text/ICU/Convert/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Convert/Internal.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE DeriveDataTypeable, EmptyDataDecls, ForeignFunctionInterface #-}
+-- |
+-- Module      : Data.Text.ICU.Convert.Internal
+-- Copyright   : (c) Bryan O'Sullivan 2009
+--
+-- License     : BSD-style
+-- Maintainer  : bos@serpentine.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Low-level character set types and functions.
+
+module Data.Text.ICU.Convert.Internal
+    (
+      Converter(..)
+    , UConverter
+    , getName
+    , withConverter
+    ) where
+
+import Data.Text.ICU.Error.Internal (UErrorCode, handleError)
+import Data.Typeable (Typeable)
+import Foreign.C.String (CString, peekCString)
+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
+import Foreign.Ptr (Ptr)
+import System.IO.Unsafe (unsafePerformIO)
+
+data UConverter
+
+-- | Character set converter type.  /Note/: this structure is not
+-- thread safe. It is /not/ safe to use value of this type
+-- simultaneously from multiple threads.
+data Converter = Converter {-# UNPACK #-} !(ForeignPtr UConverter)
+                 deriving (Eq, Typeable)
+
+instance Show Converter where
+    show c = "Converter " ++ show (getName c)
+
+withConverter :: Converter -> (Ptr UConverter -> IO a) -> IO a
+{-# INLINE withConverter #-}
+withConverter (Converter cnv) action = withForeignPtr cnv action
+
+-- | Gets the internal, canonical name of the converter.
+getName :: Converter -> String
+getName cnv = unsafePerformIO .
+  withConverter cnv $ \ptr ->
+    peekCString =<< handleError (ucnv_getName ptr)
+
+foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getName" ucnv_getName
+    :: Ptr UConverter -> Ptr UErrorCode -> IO CString
diff --git a/Data/Text/ICU/Converter.hs b/Data/Text/ICU/Converter.hs
deleted file mode 100644
--- a/Data/Text/ICU/Converter.hs
+++ /dev/null
@@ -1,235 +0,0 @@
-{-# LANGUAGE CPP, ForeignFunctionInterface #-}
--- |
--- Module      : Data.Text.ICU.Converter
--- Copyright   : (c) Bryan O'Sullivan 2009
---
--- License     : BSD-style
--- Maintainer  : bos@serpentine.com
--- Stability   : experimental
--- Portability : GHC
---
--- Character set conversion functions for Unicode, implemented as
--- bindings to the International Components for Unicode (ICU)
--- libraries.
-
-module Data.Text.ICU.Converter
-    (
-    -- * Character set conversion
-      Converter
-    -- ** Basic functions
-    , open
-    , fromUnicode
-    , toUnicode
-    -- ** Converter metadata
-    , getName
-    , usesFallback
-    , setFallback
-    , isAmbiguous
-    -- * Functions for controlling global behavior
-    , getDefaultName
-    , setDefaultName
-    -- * Miscellaneous functions
-    , compareNames
-    , aliases
-    -- * Metadata
-    , converterNames
-    , standardNames
-    ) where
-
-import Data.ByteString.Internal (ByteString, createAndTrim)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
-import Data.Int (Int32)
-import Data.Text (Text)
-import Data.Text.Foreign (fromPtr, lengthWord16, useAsPtr)
-import Data.Text.ICU.Converter.Internal
-import Data.Text.ICU.Error.Internal (UErrorCode, handleError)
-import Data.Word (Word16)
-import Foreign.C.String (CString, peekCString, withCString)
-import Foreign.C.Types (CInt)
-import Foreign.ForeignPtr (newForeignPtr)
-import Foreign.Marshal.Array (allocaArray)
-import Foreign.Ptr (FunPtr, Ptr, castPtr, nullPtr)
-import System.IO.Unsafe (unsafePerformIO)
-import Data.Text.ICU.Internal (UBool, UChar, asBool, asOrdering)
-
--- | Do a fuzzy compare of two converter/alias names.  The comparison
--- is case-insensitive, ignores leading zeroes if they are not
--- followed by further digits, and ignores all but letters and digits.
--- Thus the strings @\"UTF-8\"@, @\"utf_8\"@, @\"u*T\@f08\"@ and
--- @\"Utf 8\"@ are exactly equivalent.  See section 1.4, Charset Alias
--- Matching in Unicode Technical Standard #22 at
--- <http://www.unicode.org/reports/tr22/>
-compareNames :: String -> String -> Ordering
-compareNames a b =
-  unsafePerformIO . withCString a $ \aptr ->
-    fmap asOrdering . withCString b $ ucnv_compareNames aptr
-
--- | Create a 'Converter' with the name of a coded character set
--- specified as a string.  The actual name will be resolved with the
--- alias file using a case-insensitive string comparison that ignores
--- leading zeroes and all non-alphanumeric characters.  E.g., the
--- names @\"UTF8\"@, @\"utf-8\"@, @\"u*T\@f08\"@ and @\"Utf 8\"@ are
--- all equivalent (see also 'compareNames').  If an empty string is
--- passed for the converter name, it will create one with the
--- 'getDefaultName' return value.
---
--- A converter name may contain options like a locale specification to
--- control the specific behavior of the newly instantiated converter.
--- The meaning of the options depends on the particular converter.  If
--- an option is not defined for or recognized by a given converter,
--- then it is ignored.
---
--- Options are appended to the converter name string, with a comma
--- between the name and the first option and also between adjacent
--- options.
---
--- If the alias is ambiguous, then the preferred converter is used.
---
--- The conversion behavior and names can vary between platforms. ICU
--- may convert some characters differently from other
--- platforms. Details on this topic are in the ICU User's Guide at
--- <http://icu-project.org/userguide/conversion.html>. Aliases
--- starting with a @\"cp\"@ prefix have no specific meaning other than
--- its an alias starting with the letters @\"cp\"@. Please do not
--- associate any meaning to these aliases.
-open :: String -> IO Converter
-open name =
-  fmap Converter . newForeignPtr ucnv_close =<< named (handleError . ucnv_open)
-  where named act
-            | null name = act nullPtr
-            | otherwise = withCString name act
-
--- | Convert the Unicode string into a codepage string using the given
--- converter.
-fromUnicode :: Converter -> Text -> IO ByteString
-fromUnicode cnv t =
-  useAsPtr t $ \tptr tlen ->
-    withConverter cnv $ \cptr -> do
-      let capacity = fromIntegral . max_bytes_for_string cptr . fromIntegral $
-                     lengthWord16 t
-      createAndTrim (fromIntegral capacity) $ \sptr ->
-        fmap fromIntegral . handleError $
-           ucnv_fromUChars cptr (castPtr sptr) capacity tptr (fromIntegral tlen)
-
--- | Convert the codepage string into a Unicode string using the given
--- converter.
-toUnicode :: Converter -> ByteString -> IO Text
-toUnicode cnv bs =
-  unsafeUseAsCStringLen bs $ \(sptr, slen) ->
-    withConverter cnv $ \cptr -> do
-      let capacity = slen * 2
-      allocaArray capacity $ \tptr -> 
-        fromPtr tptr =<< (fmap fromIntegral . handleError $
-                          ucnv_toUChars cptr tptr (fromIntegral capacity) sptr
-                                        (fromIntegral slen))
-
--- | Determines whether the converter uses fallback mappings or not.
--- This flag has restrictions; see 'setFallback'.
-usesFallback :: Converter -> IO Bool
-usesFallback cnv = asBool `fmap` withConverter cnv ucnv_usesFallback
-
--- | Sets the converter to use fallback mappings or not.  Regardless
--- of this flag, the converter will always use fallbacks from Unicode
--- Private Use code points, as well as reverse fallbacks (to Unicode).
--- For details see \".ucm File Format\" in the Conversion Data chapter
--- of the ICU User Guide:
--- <http://www.icu-project.org/userguide/conversion-data.html#ucmformat>
-setFallback :: Converter -> Bool -> IO ()
-setFallback cnv flag =
-  withConverter cnv $ \p -> (ucnv_setFallback p (fromIntegral (fromEnum flag)))
-
--- | Returns the current default converter name. If you want to 'open'
--- a default converter, you do not need to use this function.  It is
--- faster to pass the empty string to 'open' the default converter.
-getDefaultName :: IO String
-getDefaultName = peekCString =<< ucnv_getDefaultName
-
--- | Indicates whether the converter contains ambiguous mappings of
--- the same character or not.
-isAmbiguous :: Converter -> Bool
-isAmbiguous cnv = asBool . unsafePerformIO $ withConverter cnv ucnv_isAmbiguous
-
--- | Sets the current default converter name. If this function needs
--- to be called, it should be called during application
--- initialization. Most of the time, the results from 'getDefaultName'
--- or 'open' with an empty string argument is sufficient for your
--- application.
---
--- /Note/: this function is not thread safe. /Do not/ call this
--- function when /any/ ICU function is being used from more than one
--- thread!
-setDefaultName :: String -> IO ()
-setDefaultName s = withCString s $ ucnv_setDefaultName
-
--- | A list of the canonical names of all available converters.
-converterNames :: [String]
-{-# NOINLINE converterNames #-}
-converterNames = unsafePerformIO $
-  mapM ((peekCString =<<) . ucnv_getAvailableName) [0..ucnv_countAvailable-1]
-
--- | The list of supported standard names.
-standardNames :: [String]
-{-# NOINLINE standardNames #-}
-standardNames = filter (not . null) . unsafePerformIO $
-  mapM ((peekCString =<<) . handleError . ucnv_getStandard) [0..ucnv_countStandards-1]
-
--- | Return the aliases for a given converter or alias name.
-aliases :: String -> [String]
-aliases name = unsafePerformIO . withCString name $ \ptr -> do
-  count <- ucnv_countAliases ptr
-  if count == 0
-    then return []
-    else mapM ((peekCString =<<) . handleError . ucnv_getAlias ptr) [0..count-1]
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_open" ucnv_open
-    :: CString -> Ptr UErrorCode -> IO (Ptr UConverter)
-
-foreign import ccall unsafe "hs_text_icu.h &__hs_ucnv_close" ucnv_close
-    :: FunPtr (Ptr UConverter -> IO ())
-
-foreign import ccall unsafe "__get_max_bytes_for_string" max_bytes_for_string
-    :: Ptr UConverter -> CInt -> CInt
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_toUChars" ucnv_toUChars
-    :: Ptr UConverter -> Ptr UChar -> Int32 -> CString -> Int32
-    -> Ptr UErrorCode -> IO Int32
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_fromUChars" ucnv_fromUChars
-    :: Ptr UConverter -> CString -> Int32 -> Ptr UChar -> Int32
-    -> Ptr UErrorCode -> IO Int32
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_compareNames" ucnv_compareNames
-    :: CString -> CString -> IO CInt
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getDefaultName" ucnv_getDefaultName
-    :: IO CString
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_setDefaultName" ucnv_setDefaultName
-    :: CString -> IO ()
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_countAvailable" ucnv_countAvailable
-    :: Int32
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getAvailableName" ucnv_getAvailableName
-    :: Int32 -> IO CString
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_countAliases" ucnv_countAliases
-    :: CString -> IO Word16
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getAlias" ucnv_getAlias
-    :: CString -> Word16 -> Ptr UErrorCode -> IO CString
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_countStandards" ucnv_countStandards
-    :: Word16
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getStandard" ucnv_getStandard
-    :: Word16 -> Ptr UErrorCode -> IO CString
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_usesFallback" ucnv_usesFallback
-    :: Ptr UConverter -> IO UBool
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_setFallback" ucnv_setFallback
-    :: Ptr UConverter -> UBool -> IO ()
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_isAmbiguous" ucnv_isAmbiguous
-    :: Ptr UConverter -> IO UBool
diff --git a/Data/Text/ICU/Converter/Internal.hs b/Data/Text/ICU/Converter/Internal.hs
deleted file mode 100644
--- a/Data/Text/ICU/Converter/Internal.hs
+++ /dev/null
@@ -1,50 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable, EmptyDataDecls, ForeignFunctionInterface #-}
--- |
--- Module      : Data.Text.ICU.Converter.Internal
--- Copyright   : (c) Bryan O'Sullivan 2009
---
--- License     : BSD-style
--- Maintainer  : bos@serpentine.com
--- Stability   : experimental
--- Portability : GHC
---
--- Low-level character set types and functions.
-
-module Data.Text.ICU.Converter.Internal
-    (
-      Converter(..)
-    , UConverter
-    , getName
-    , withConverter
-    ) where
-
-import Data.Text.ICU.Error.Internal (UErrorCode, handleError)
-import Data.Typeable (Typeable)
-import Foreign.C.String (CString, peekCString)
-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
-import Foreign.Ptr (Ptr)
-import System.IO.Unsafe (unsafePerformIO)
-
-data UConverter
-
--- | Character set converter type.  /Note/: this structure is not
--- thread safe. It is /not/ safe to use value of this type
--- simultaneously from multiple threads.
-data Converter = Converter {-# UNPACK #-} !(ForeignPtr UConverter)
-                 deriving (Eq, Typeable)
-
-instance Show Converter where
-    show c = "Converter " ++ show (getName c)
-
-withConverter :: Converter -> (Ptr UConverter -> IO a) -> IO a
-{-# INLINE withConverter #-}
-withConverter (Converter cnv) action = withForeignPtr cnv action
-
--- | Gets the internal, canonical name of the converter.
-getName :: Converter -> String
-getName cnv = unsafePerformIO .
-  withConverter cnv $ \ptr ->
-    peekCString =<< handleError (ucnv_getName ptr)
-
-foreign import ccall unsafe "hs_text_icu.h __hs_ucnv_getName" ucnv_getName
-    :: Ptr UConverter -> Ptr UErrorCode -> IO CString
diff --git a/Data/Text/ICU/Error/Internal.hs b/Data/Text/ICU/Error/Internal.hs
--- a/Data/Text/ICU/Error/Internal.hs
+++ b/Data/Text/ICU/Error/Internal.hs
@@ -15,13 +15,13 @@
     , withError
     ) where
 
-import Control.Exception
-import Foreign.Ptr
-import Foreign.Marshal.Alloc
+import Control.Exception (Exception, throw)
+import Foreign.Ptr (Ptr)
+import Foreign.Marshal.Alloc (alloca)
 import Data.Typeable (Typeable)
 import Foreign.C.String (CString, peekCString)
 import Foreign.C.Types (CInt)
-import Foreign.Storable
+import Foreign.Storable (peek, poke)
 import System.IO.Unsafe (unsafePerformIO)
 
 type UErrorCode = CInt
diff --git a/Data/Text/ICU/Normalize.hsc b/Data/Text/ICU/Normalize.hsc
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Normalize.hsc
@@ -0,0 +1,308 @@
+{-# LANGUAGE CPP, DeriveDataTypeable, ForeignFunctionInterface,
+    GeneralizedNewtypeDeriving #-}
+-- |
+-- Module      : Data.Text.ICU.Normalize
+-- Copyright   : (c) Bryan O'Sullivan 2009
+--
+-- License     : BSD-style
+-- Maintainer  : bos@serpentine.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Character set normalization functions for Unicode, implemented as
+-- bindings to the International Components for Unicode (ICU)
+-- libraries.
+
+module Data.Text.ICU.Normalize
+    (
+    -- * Unicode normalization API
+    -- $api
+      NormalizationMode(..)
+    -- * Normalization functions
+    , normalize
+    -- * Normalization checks
+    , NormalizationCheckResult(..)
+    , quickCheck
+    , isNormalized
+    -- * Normalization-sensitive comparison
+    , CompareOption(..)
+    , compare
+    ) where
+
+#ifdef mingw32_HOST_OS
+#define U_HAVE_INTTYPES_H 1
+#endif
+
+#include <unicode/uchar.h>
+#include <unicode/unorm.h>
+
+import Control.Exception (throw)
+import Control.Monad (when)
+import Data.Text (Text)
+import Data.Text.Foreign (fromPtr, useAsPtr)
+import Data.Text.ICU.Error (u_BUFFER_OVERFLOW_ERROR)
+import Data.Text.ICU.Error.Internal (UErrorCode, isFailure, handleError, withError)
+import Data.Text.ICU.Internal (UBool, UChar, asBool, asOrdering)
+import Data.Typeable (Typeable)
+import Data.Int (Int32)
+import Data.Word (Word32)
+import Foreign.C.Types (CInt)
+import Foreign.Marshal.Array (allocaArray)
+import Foreign.Ptr (Ptr)
+import System.IO.Unsafe (unsafePerformIO)
+import Prelude hiding (compare)
+import Data.List (foldl')
+import Data.Bits ((.|.))
+
+-- $api
+--
+-- 'normalize' transforms Unicode text into an equivalent composed or
+-- decomposed form, allowing for easier sorting and searching of text.
+-- 'normalize' supports the standard normalization forms described in
+-- <http://www.unicode.org/unicode/reports/tr15/>,
+-- Unicode Standard Annex #15: Unicode Normalization Forms.
+--
+-- Characters with accents or other adornments can be encoded in
+-- several different ways in Unicode.  For example, take the character A-acute.
+-- In Unicode, this can be encoded as a single character (the
+-- \"composed\" form):
+--
+-- @
+--      00C1    LATIN CAPITAL LETTER A WITH ACUTE
+-- @
+--
+-- or as two separate characters (the \"decomposed\" form):
+--
+-- @
+--      0041    LATIN CAPITAL LETTER A
+--      0301    COMBINING ACUTE ACCENT
+-- @
+--
+-- To a user of your program, however, both of these sequences should
+-- be treated as the same \"user-level\" character \"A with acute
+-- accent\".  When you are searching or comparing text, you must
+-- ensure that these two sequences are treated equivalently.  In
+-- addition, you must handle characters with more than one accent.
+-- Sometimes the order of a character's combining accents is
+-- significant, while in other cases accent sequences in different
+-- orders are really equivalent.
+--
+-- Similarly, the string \"ffi\" can be encoded as three separate letters:
+--
+-- @
+--      0066    LATIN SMALL LETTER F
+--      0066    LATIN SMALL LETTER F
+--      0069    LATIN SMALL LETTER I
+-- @
+--
+-- or as the single character
+--
+-- @
+--      FB03    LATIN SMALL LIGATURE FFI
+-- @
+--
+-- The \"ffi\" ligature is not a distinct semantic character, and
+-- strictly speaking it shouldn't be in Unicode at all, but it was
+-- included for compatibility with existing character sets that
+-- already provided it.  The Unicode standard identifies such
+-- characters by giving them \"compatibility\" decompositions into the
+-- corresponding semantic characters.  When sorting and searching, you
+-- will often want to use these mappings.
+--
+-- 'normalize' helps solve these problems by transforming text into
+-- the canonical composed and decomposed forms as shown in the first
+-- example above.  In addition, you can have it perform compatibility
+-- decompositions so that you can treat compatibility characters the
+-- same as their equivalents.  Finally, 'normalize' rearranges accents
+-- into the proper canonical order, so that you do not have to worry
+-- about accent rearrangement on your own.
+--
+-- Form 'FCD', \"Fast C or D\", is also designed for collation.  It
+-- allows to work on strings that are not necessarily normalized with
+-- an algorithm (like in collation) that works under \"canonical
+-- closure\", i.e., it treats precomposed characters and their
+-- decomposed equivalents the same.
+--
+-- It is not a normalization form because it does not provide for
+-- uniqueness of representation. Multiple strings may be canonically
+-- equivalent (their NFDs are identical) and may all conform to 'FCD'
+-- without being identical themselves.
+--
+-- The form is defined such that the \"raw decomposition\", the
+-- recursive canonical decomposition of each character, results in a
+-- string that is canonically ordered. This means that precomposed
+-- characters are allowed for as long as their decompositions do not
+-- need canonical reordering.
+--
+-- Its advantage for a process like collation is that all 'NFD' and
+-- most 'NFC' texts - and many unnormalized texts - already conform to
+-- 'FCD' and do not need to be normalized ('NFD') for such a
+-- process. The 'FCD' 'quickCheck' will return 'Yes' for most strings
+-- in practice.
+--
+-- @'normalize' 'FCD'@ may be implemented with 'NFD'.
+--
+-- For more details on 'FCD' see the collation design document:
+-- <http://source.icu-project.org/repos/icu/icuhtml/trunk/design/collation/ICU_collation_design.htm>
+--
+-- ICU collation performs either 'NFD' or 'FCD' normalization
+-- automatically if normalization is turned on for the collator
+-- object.  Beyond collation and string search, normalized strings may
+-- be useful for string equivalence comparisons,
+-- transliteration/transcription, unique representations, etc.
+--
+-- The W3C generally recommends to exchange texts in 'NFC'.  Note also
+-- that most legacy character encodings use only precomposed forms and
+-- often do not encode any combining marks by themselves. For
+-- conversion to such character encodings the Unicode text needs to be
+-- normalized to 'NFC'.  For more usage examples, see the Unicode
+-- Standard Annex.
+
+type UCompareOption = Word32
+
+-- | Options to 'compare'.
+data CompareOption = InputIsFCD
+                   -- ^ The caller knows that both strings fulfill the
+                   -- 'FCD' conditions.  If not set, 'compare' will
+                   -- 'quickCheck' for 'FCD' and normalize if
+                   -- necessary.
+                   | CompareCodePointOrder
+                   -- ^ Choose code point order instead of code unit
+                   -- order.
+                   | CompareIgnoreCase
+                   -- ^ Compare strings case-insensitively using case
+                   -- folding, instead of case-sensitively.  If set,
+                   -- then the following case folding options are
+                   -- used.
+                   | FoldCaseExcludeSpecialI
+                   -- ^ When case folding, exclude the special I
+                   -- character.  For use with Turkic
+                   -- (Turkish/Azerbaijani) text data.
+                     deriving (Eq, Show, Enum, Typeable)
+
+fromCompareOption :: CompareOption -> UCompareOption
+fromCompareOption InputIsFCD              = #const UNORM_INPUT_IS_FCD
+fromCompareOption CompareCodePointOrder   = #const U_COMPARE_CODE_POINT_ORDER
+fromCompareOption CompareIgnoreCase       = #const U_COMPARE_IGNORE_CASE
+fromCompareOption FoldCaseExcludeSpecialI = #const U_FOLD_CASE_EXCLUDE_SPECIAL_I
+
+reduceCompareOptions :: [CompareOption] -> UCompareOption
+reduceCompareOptions = foldl' orO (#const U_FOLD_CASE_DEFAULT)
+    where a `orO` b = a .|. fromCompareOption b
+
+type UNormalizationMode = CInt
+
+-- | Normalization modes.
+data NormalizationMode
+    = None   -- ^ No decomposition/composition.
+    | NFD    -- ^ Canonical decomposition.
+    | NFKD   -- ^ Compatibility decomposition.
+    | NFC    -- ^ Canonical decomposition followed by canonical composition.
+    | NFKC   -- ^ Compatibility decomposition followed by canonical composition.
+    | FCD    -- ^ \"Fast C or D\" form.
+      deriving (Eq, Show, Enum, Typeable)
+                       
+toNM :: NormalizationMode -> UNormalizationMode
+toNM None = #const UNORM_NONE
+toNM NFD  = #const UNORM_NFD
+toNM NFKD = #const UNORM_NFKD
+toNM NFC  = #const UNORM_NFC
+toNM NFKC = #const UNORM_NFKC
+toNM FCD  = #const UNORM_FCD
+
+type UNormalizationCheckResult = CInt
+
+-- | Result of a fast normalization check using 'quickCheck'.
+data NormalizationCheckResult
+    = No      -- ^ Text is not normalized.
+    | Perhaps -- ^ It cannot be determined whether text is in normalized 
+              -- form without further thorough checks.
+    | Yes     -- ^ Text is in normalized form.
+      deriving (Eq, Show, Enum, Typeable)
+                              
+toNCR :: UNormalizationCheckResult -> NormalizationCheckResult
+toNCR (#const UNORM_NO)    = No
+toNCR (#const UNORM_MAYBE) = Perhaps
+toNCR (#const UNORM_YES)   = Yes
+toNCR _                    = error "toNormalizationCheckResult"
+
+-- | Normalize a string according the specified normalization mode.
+normalize :: NormalizationMode -> Text -> Text
+normalize mode t = unsafePerformIO . useAsPtr t $ \sptr slen ->
+  let slen' = fromIntegral slen
+      mode' = toNM mode
+      loop dlen =
+        (either loop return =<<) .
+        allocaArray dlen $ \dptr -> do
+          (err, newLen) <- withError $
+              unorm_normalize sptr slen' mode' 0 dptr (fromIntegral dlen)
+          when (isFailure err && err /= u_BUFFER_OVERFLOW_ERROR) $
+            throw err
+          let newLen' = fromIntegral newLen
+          if newLen' > dlen
+            then return (Left newLen')
+            else Right `fmap` fromPtr dptr newLen'
+  in loop slen
+    
+      
+-- | Perform an efficient check on a string, to quickly determine if
+-- the string is in a particular normalization format.
+--
+-- A 'Perhaps' result indicates that a more thorough check is
+-- required, e.g. with 'isNormalized'.  The user may have to put the
+-- string in its normalized form and compare the results.
+quickCheck :: NormalizationMode -> Text -> NormalizationCheckResult
+quickCheck mode t =
+  unsafePerformIO . useAsPtr t $ \ptr len ->
+    fmap toNCR . handleError $ unorm_quickCheck ptr (fromIntegral len)
+                               (toNM mode)
+
+-- | Indicate whether a string is in a given normalization form.
+--
+-- Unlike 'quickCheck', this function returns a definitive result.
+-- For 'NFD', 'NFKD', and 'FCD' normalization forms, both functions
+-- work in exactly the same ways.  For 'NFC' and 'NFKC' forms, where
+-- 'quickCheck' may return 'Perhaps', this function will perform
+-- further tests to arrive at a definitive result.
+isNormalized :: NormalizationMode -> Text -> Bool
+isNormalized mode t =
+  unsafePerformIO . useAsPtr t $ \ptr len ->
+    fmap asBool . handleError $ unorm_isNormalized ptr (fromIntegral len)
+                                (toNM mode)
+
+-- | Compare two strings for canonical equivalence.  Further options
+-- include case-insensitive comparison and code point order (as
+-- opposed to code unit order).
+--
+-- Canonical equivalence between two strings is defined as their
+-- normalized forms ('NFD' or 'NFC') being identical.  This function
+-- compares strings incrementally instead of normalizing (and
+-- optionally case-folding) both strings entirely, improving
+-- performance significantly.
+--
+-- Bulk normalization is only necessary if the strings do not fulfill
+-- the FCD conditions. Only in this case, and only if the strings are
+-- relatively long, is memory allocated temporarily.  For FCD strings
+-- and short non-FCD strings there is no memory allocation.
+compare :: [CompareOption] -> Text -> Text -> Ordering
+compare opts a b = unsafePerformIO .
+  useAsPtr a $ \aptr alen ->
+    useAsPtr b $ \bptr blen ->
+      fmap asOrdering . handleError $
+      unorm_compare aptr (fromIntegral alen) bptr (fromIntegral blen)
+                    (reduceCompareOptions opts)
+
+foreign import ccall unsafe "hs_text_icu.h __hs_unorm_compare" unorm_compare
+    :: Ptr UChar -> Int32 -> Ptr UChar -> Int32 -> Word32
+    -> Ptr UErrorCode -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_unorm_quickCheck" unorm_quickCheck
+    :: Ptr UChar -> Int32 -> UNormalizationMode -> Ptr UErrorCode
+    -> IO UNormalizationCheckResult
+
+foreign import ccall unsafe "hs_text_icu.h __hs_unorm_isNormalized" unorm_isNormalized
+    :: Ptr UChar -> Int32 -> UNormalizationMode -> Ptr UErrorCode -> IO UBool
+
+foreign import ccall unsafe "hs_text_icu.h __hs_unorm_normalize" unorm_normalize
+    :: Ptr UChar -> Int32 -> UNormalizationMode -> Int32
+    -> Ptr UChar -> Int32 -> Ptr UErrorCode -> IO Int32
diff --git a/Data/Text/ICU/Normalizer.hsc b/Data/Text/ICU/Normalizer.hsc
deleted file mode 100644
--- a/Data/Text/ICU/Normalizer.hsc
+++ /dev/null
@@ -1,303 +0,0 @@
-{-# LANGUAGE CPP, DeriveDataTypeable, ForeignFunctionInterface,
-    GeneralizedNewtypeDeriving #-}
--- |
--- Module      : Data.Text.ICU.Normalizer
--- Copyright   : (c) Bryan O'Sullivan 2009
---
--- License     : BSD-style
--- Maintainer  : bos@serpentine.com
--- Stability   : experimental
--- Portability : GHC
---
--- Character set normalization functions for Unicode, implemented as
--- bindings to the International Components for Unicode (ICU)
--- libraries.
-
-module Data.Text.ICU.Normalizer
-    (
-    -- * Unicode normalization API
-    -- $api
-      NormalizationMode(..)
-    -- * Normalization functions
-    , normalize
-    -- * Normalization checks
-    , NormalizationCheckResult(..)
-    , quickCheck
-    , isNormalized
-    -- * Normalization-sensitive comparison
-    , CompareOption
-    , u_INPUT_IS_FCD
-    , u_COMPARE_CODE_POINT_ORDER
-    , u_COMPARE_IGNORE_CASE
-    , compare
-    ) where
-
-#ifdef mingw32_HOST_OS
-#define U_HAVE_INTTYPES_H 1
-#endif
-
-#include <unicode/unorm.h>
-
-import Control.Exception (throw)
-import Control.Monad (when)
-import Data.Text (Text)
-import Data.Text.Foreign (fromPtr, useAsPtr)
-import Data.Text.ICU.Error (u_BUFFER_OVERFLOW_ERROR)
-import Data.Text.ICU.Error.Internal (UErrorCode, isFailure, handleError, withError)
-import Data.Text.ICU.Internal (UBool, UChar, asBool, asOrdering)
-import Data.Typeable (Typeable)
-import Data.Int (Int32)
-import Data.Word (Word32)
-import Foreign.C.Types (CInt)
-import Foreign.Marshal.Array (allocaArray)
-import Foreign.Ptr (Ptr)
-import System.IO.Unsafe (unsafePerformIO)
-import Prelude hiding (compare)
-import Data.List (foldl')
-import Data.Bits ((.|.))
-
--- $api
---
--- 'normalize' transforms Unicode text into an equivalent composed or
--- decomposed form, allowing for easier sorting and searching of text.
--- 'normalize' supports the standard normalization forms described in
--- <http://www.unicode.org/unicode/reports/tr15/>,
--- Unicode Standard Annex #15: Unicode Normalization Forms.
---
--- Characters with accents or other adornments can be encoded in
--- several different ways in Unicode.  For example, take the character A-acute.
--- In Unicode, this can be encoded as a single character (the
--- \"composed\" form):
---
--- @
---      00C1    LATIN CAPITAL LETTER A WITH ACUTE
--- @
---
--- or as two separate characters (the \"decomposed\" form):
---
--- @
---      0041    LATIN CAPITAL LETTER A
---      0301    COMBINING ACUTE ACCENT
--- @
---
--- To a user of your program, however, both of these sequences should
--- be treated as the same \"user-level\" character \"A with acute
--- accent\".  When you are searching or comparing text, you must
--- ensure that these two sequences are treated equivalently.  In
--- addition, you must handle characters with more than one accent.
--- Sometimes the order of a character's combining accents is
--- significant, while in other cases accent sequences in different
--- orders are really equivalent.
---
--- Similarly, the string \"ffi\" can be encoded as three separate letters:
---
--- @
---      0066    LATIN SMALL LETTER F
---      0066    LATIN SMALL LETTER F
---      0069    LATIN SMALL LETTER I
--- @
---
--- or as the single character
---
--- @
---      FB03    LATIN SMALL LIGATURE FFI
--- @
---
--- The \"ffi\" ligature is not a distinct semantic character, and
--- strictly speaking it shouldn't be in Unicode at all, but it was
--- included for compatibility with existing character sets that
--- already provided it.  The Unicode standard identifies such
--- characters by giving them \"compatibility\" decompositions into the
--- corresponding semantic characters.  When sorting and searching, you
--- will often want to use these mappings.
---
--- 'normalize' helps solve these problems by transforming text into
--- the canonical composed and decomposed forms as shown in the first
--- example above.  In addition, you can have it perform compatibility
--- decompositions so that you can treat compatibility characters the
--- same as their equivalents.  Finally, 'normalize' rearranges accents
--- into the proper canonical order, so that you do not have to worry
--- about accent rearrangement on your own.
---
--- Form 'FCD', \"Fast C or D\", is also designed for collation.  It
--- allows to work on strings that are not necessarily normalized with
--- an algorithm (like in collation) that works under \"canonical
--- closure\", i.e., it treats precomposed characters and their
--- decomposed equivalents the same.
---
--- It is not a normalization form because it does not provide for
--- uniqueness of representation. Multiple strings may be canonically
--- equivalent (their NFDs are identical) and may all conform to 'FCD'
--- without being identical themselves.
---
--- The form is defined such that the \"raw decomposition\", the
--- recursive canonical decomposition of each character, results in a
--- string that is canonically ordered. This means that precomposed
--- characters are allowed for as long as their decompositions do not
--- need canonical reordering.
---
--- Its advantage for a process like collation is that all 'NFD' and
--- most 'NFC' texts - and many unnormalized texts - already conform to
--- 'FCD' and do not need to be normalized ('NFD') for such a
--- process. The 'FCD' 'quickCheck' will return 'Yes' for most strings
--- in practice.
---
--- @'normalize' 'FCD'@ may be implemented with 'NFD'.
---
--- For more details on 'FCD' see the collation design document:
--- <http://source.icu-project.org/repos/icu/icuhtml/trunk/design/collation/ICU_collation_design.htm>
---
--- ICU collation performs either 'NFD' or 'FCD' normalization
--- automatically if normalization is turned on for the collator
--- object.  Beyond collation and string search, normalized strings may
--- be useful for string equivalence comparisons,
--- transliteration/transcription, unique representations, etc.
---
--- The W3C generally recommends to exchange texts in 'NFC'.  Note also
--- that most legacy character encodings use only precomposed forms and
--- often do not encode any combining marks by themselves. For
--- conversion to such character encodings the Unicode text needs to be
--- normalized to 'NFC'.  For more usage examples, see the Unicode
--- Standard Annex.
-
-newtype CompareOption = CompareOption {
-      fromCompareOption :: Word32
-    } deriving (Eq, Typeable)
-
-foldCompareOptions :: [CompareOption] -> Word32
-foldCompareOptions = foldl' orO 0
-    where a `orO` b = a .|. fromCompareOption b
-
-#{enum CompareOption, CompareOption,
-  u_INPUT_IS_FCD = UNORM_INPUT_IS_FCD,
-  u_COMPARE_CODE_POINT_ORDER = U_COMPARE_CODE_POINT_ORDER,
-  u_COMPARE_IGNORE_CASE = U_COMPARE_IGNORE_CASE
-  }
-
-type UNormalizationMode = CInt
-
-data NormalizationMode
-    = None   -- ^ No decomposition/composition.
-    | NFD    -- ^ Canonical decomposition.
-    | NFKD   -- ^ Compatibility decomposition.
-    | NFC    -- ^ Canonical decomposition followed by canonical composition.
-    | NFKC   -- ^ Compatibility decomposition followed by canonical composition.
-    | FCD    -- ^ \"Fast C or D\" form.
-      deriving (Eq, Show, Enum, Typeable)
-                       
-toNM :: NormalizationMode -> UNormalizationMode
-toNM None = #const UNORM_NONE
-toNM NFD  = #const UNORM_NFD
-toNM NFKD = #const UNORM_NFKD
-toNM NFC  = #const UNORM_NFC
-toNM NFKC = #const UNORM_NFKC
-toNM FCD  = #const UNORM_FCD
-
-type UNormalizationCheckResult = CInt
-
--- | Result of a fast normalization check using 'quickCheck'.
-data NormalizationCheckResult
-    = No      -- ^ Text is not normalized.
-    | Perhaps -- ^ It cannot be determined whether text is in normalized 
-              -- form without further thorough checks.
-    | Yes     -- ^ Text is in normalized form.
-      deriving (Eq, Show, Enum, Typeable)
-                              
-toNCR :: UNormalizationCheckResult -> NormalizationCheckResult
-toNCR (#const UNORM_NO)    = No
-toNCR (#const UNORM_MAYBE) = Perhaps
-toNCR (#const UNORM_YES)   = Yes
-toNCR _                    = error "toNormalizationCheckResult"
-
--- | Normalize a string according the specified normalization mode.
-normalize :: NormalizationMode -> Text -> Text
-normalize mode t = unsafePerformIO . useAsPtr t $ \sptr slen ->
-  let slen' = fromIntegral slen
-      mode' = toNM mode
-      loop dlen =
-        (either loop return =<<) .
-        allocaArray dlen $ \dptr -> do
-          (err, newLen) <- withError $
-              unorm_normalize sptr slen' mode' 0 dptr (fromIntegral dlen)
-          when (isFailure err && err /= u_BUFFER_OVERFLOW_ERROR) $
-            throw err
-          let newLen' = fromIntegral newLen
-          if newLen' > dlen
-            then return (Left newLen')
-            else Right `fmap` fromPtr dptr newLen'
-  in loop slen
-    
-      
--- | Perform an efficient check on a string, to quickly determine if
--- the string is in a particular normalization format.
---
--- A 'Perhaps' result indicates that a more thorough check is
--- required, e.g. with 'isNormalized'.  The user may have to put the
--- string in its normalized form and compare the results.
-quickCheck :: NormalizationMode -> Text -> NormalizationCheckResult
-quickCheck mode t =
-  unsafePerformIO . useAsPtr t $ \ptr len ->
-    fmap toNCR . handleError $ unorm_quickCheck ptr (fromIntegral len)
-                               (toNM mode)
-
--- | Indicate whether a string is in a given normalization form.
---
--- Unlike 'quickCheck', this function returns a definitive result.
--- For 'NFD', 'NFKD', and 'FCD' normalization forms, both functions
--- work in exactly the same ways.  For 'NFC' and 'NFKC' forms, where
--- 'quickCheck' may return 'Perhaps', this function will perform
--- further tests to arrive at a definitive result.
-isNormalized :: NormalizationMode -> Text -> Bool
-isNormalized mode t =
-  unsafePerformIO . useAsPtr t $ \ptr len ->
-    fmap asBool . handleError $ unorm_isNormalized ptr (fromIntegral len)
-                                (toNM mode)
-
--- | Compare two strings for canonical equivalence.
--- Further options include case-insensitive comparison and
--- code point order (as opposed to code unit order).
---
--- Canonical equivalence between two strings is defined as their normalized
--- forms (NFD or NFC) being identical.
--- This function compares strings incrementally instead of normalizing
--- (and optionally case-folding) both strings entirely,
--- improving performance significantly.
---
--- Bulk normalization is only necessary if the strings do not fulfill the FCD
--- conditions. Only in this case, and only if the strings are relatively long,
--- is memory allocated temporarily.
--- For FCD strings and short non-FCD strings there is no memory allocation.
---
--- Options:
---
---  ['u_INPUT_IS_FCD'] Set if the caller knows that both strings fulfill the FCD conditions.
---     If not set, the function will 'quickCheck' for FCD
---     and normalize if necessary.
---
---  ['u_COMPARE_CODE_POINT_ORDER'] Set to choose code point order instead of code unit order.
---
---  ['u_COMPARE_IGNORE_CASE'] Set to compare strings case-insensitively using case folding,
---     instead of case-sensitively.
---     If set, then the following case folding options are used.
-compare :: [CompareOption] -> Text -> Text -> Ordering
-compare opts a b = unsafePerformIO .
-  useAsPtr a $ \aptr alen ->
-    useAsPtr b $ \bptr blen ->
-      fmap asOrdering . handleError $
-      unorm_compare aptr (fromIntegral alen) bptr (fromIntegral blen)
-                    (foldCompareOptions opts)
-
-foreign import ccall unsafe "hs_text_icu.h __hs_unorm_compare" unorm_compare
-    :: Ptr UChar -> Int32 -> Ptr UChar -> Int32 -> Word32
-    -> Ptr UErrorCode -> IO Int32
-
-foreign import ccall unsafe "hs_text_icu.h __hs_unorm_quickCheck" unorm_quickCheck
-    :: Ptr UChar -> Int32 -> UNormalizationMode -> Ptr UErrorCode
-    -> IO UNormalizationCheckResult
-
-foreign import ccall unsafe "hs_text_icu.h __hs_unorm_isNormalized" unorm_isNormalized
-    :: Ptr UChar -> Int32 -> UNormalizationMode -> Ptr UErrorCode -> IO UBool
-
-foreign import ccall unsafe "hs_text_icu.h __hs_unorm_normalize" unorm_normalize
-    :: Ptr UChar -> Int32 -> UNormalizationMode -> Int32
-    -> Ptr UChar -> Int32 -> Ptr UErrorCode -> IO Int32
diff --git a/text-icu.cabal b/text-icu.cabal
--- a/text-icu.cabal
+++ b/text-icu.cabal
@@ -1,5 +1,5 @@
 name:           text-icu
-version:        0.2.0.0
+version:        0.3.0.0
 synopsis:       Bindings to the ICU library
 description:    Haskell bindings to the International Components for
                 Unicode (ICU) libraries.  These libraries provide
@@ -21,11 +21,11 @@
     build-depends:   base >= 4
 
   exposed-modules:
-      Data.Text.ICU.Converter
+      Data.Text.ICU.Convert
       Data.Text.ICU.Error
-      Data.Text.ICU.Normalizer
+      Data.Text.ICU.Normalize
   other-modules:
-      Data.Text.ICU.Converter.Internal
+      Data.Text.ICU.Convert.Internal
       Data.Text.ICU.Error.Internal
       Data.Text.ICU.Internal
   c-sources: cbits/text_icu.c
