content: module Codec.Beam.Internal.Table
    ( Table, empty, singleton, index, ensure, encode, size
    ) where
  
  import Prelude hiding (map)
  import qualified Data.Map as Map
  import qualified Data.List as List
  
  
  data Table k
    = Table
        { _offset :: Int
        , _map :: Map.Map k Int
        }
  
  
  empty :: Table k
  empty =
    Table 0 Map.empty
  
  
  {-| The atom table seems to follow some different rules.
   -  I\'m surprised '_offset' is needed at all, but without it there are
   -  off-by-one encoding errors.
   -}
  singleton :: k -> Int -> Table k
  singleton key initialValue =
    Table initialValue (Map.singleton key initialValue)
  
  
  index :: Ord k => k -> Table k -> (Int, Table k)
  index key table@(Table offset map) =
    case Map.lookup key map of
      Just value ->
        (value, table)
  
      Nothing ->
        let value = Map.size map + offset in
        (value, table { _map = Map.insert key value map })
  
  
  ensure :: Ord k => k -> Table k -> Table k
  ensure key =
    snd . index key
  
  
  encode :: Monoid m => (k -> m) -> Table k -> m
  encode func (Table _ map) =
    mconcat
      $ fmap (func . fst)
      $ List.sortOn snd
      $ Map.toList map
  
  
  size :: Table k -> Int
  size (Table _ map) =
    Map.size map
  