Spec-Zone.ru › Haskell 7

Data.ByteString.Builder.Prim

Copyright (c) 2010-2011 Simon Meier (c) 2010 Jasper van der Jeugt
License BSD3-style (see LICENSE)
Maintainer Simon Meier <iridcode@gmail.com>
Portability GHC
Safe Haskell Trustworthy
Language Haskell98

Содержание

  • Примитивы ограниченного размера
    • Комбинаторы
    • Построение билдера
  • Примитивы фиксированного размера
    • Комбинаторы
    • Построение билдера
  • Стандартные кодирования значений Haskell
    • Бинарные кодирования
      • Big-endian
      • Little-endian
      • Непереносимые, зависящие от платформы
    • Кодирования символов
      • ASCII
        • Десятичные числа
        • Шестнадцатеричные числа
        • Шестнадцатеричные числа фиксированной ширины
      • ISO/IEC 8859-1 (Char8)
      • UTF-8

Описание

Этот модуль предоставляет Builder примитивы, которые являются блоками более низкого уровня для построения Builder. Вам не обязательно опускаться на этот уровень, но это может быть немного быстрее.

По сути, примитивы билдера подобны функциям a -> Builder, то есть они принимают значение и кодируют его как последовательность байтов, представленных как Builder. Конечно, их реализация немного более специализирована.

Примитивы билдера бывают двух типов: фиксированного размера и ограниченного размера.

  • Примитивы фиксированного размера — это примитивы билдера, которые всегда приводят к последовательности байтов фиксированной длины. То есть длина не зависит от кодируемого значения. Примером примитива фиксированного размера является big-endian кодирование Word64, которое всегда приводит к 8 байтам.
  • Примитивы ограниченного размера — это примитивы билдера, которые всегда приводят к последовательности байтов, длина которой не превышает предопределённого предела. То есть предел не зависит от кодируемого значения, но фактическая длина будет зависеть от значения. Пример примитива ограниченного размера — UTF-8 кодирование Char, которое может иметь длину 1, 2, 3 или 4 байта, так что предел составляет 4 байта.

Обратите внимание, что примитивы фиксированного размера можно рассматривать как частный случай примитивов ограниченного размера, и мы можем перейти от фиксированного размера к ограниченному.

Поскольку примитивы ограниченного размера являются более общим случаем, в этом документе мы ссылаемся только на примитивы фиксированного размера, когда важно, что результирующая последовательность байтов имеет фиксированную длину. В противном случае мы просто ссылаемся на примитивы ограниченного размера.

Цель использования примитивов билдера — улучшить производительность Builder. Эти улучшения происходят из повышения эффективности двух наиболее распространённых операций, выполняемых Builder . Мы объясним эти два этапа по порядку.

Первый наиболее распространённый этап — конкатенация двух Builder. Внутренне конкатенация соответствует композиции функций. (Обратите внимание, что Builder можно рассматривать как списки разниц функций заполнения буфера; см. http://hackage.haskell.org/cgi-bin/hackage-scripts/package/dlist. ) Композиция функций — быстрая операция O(1). Однако мы можем использовать примитивы ограниченного размера, чтобы избежать некоторых из этих композиций функций, что более эффективно.

Вторая наиболее распространённая операция, выполняемая Builder , — заполнение буфера с помощью примитивов ограниченного размера, что работает следующим образом. Builder проверяет, достаточно ли места, чтобы выполнить примитив ограниченного размера. Если да, то Builder выполняет примитив ограниченного размера и вызывает следующую Builder с обновлённым буфером. В противном случае Builder сигнализирует своему драйверу, что ему требуется новый буфер. Этот буфер должен быть как минимум таким же большим, как предел примитива. Мы можем использовать примитивы ограниченного размера, чтобы уменьшить количество проверок на отсутствие буфера, объединяя проверки на отсутствие буфера последовательных Builder . Мы также можем использовать примитивы ограниченного размера, чтобы упростить управление потоком для сигнализации о том, что буфер заполнен, убедившись, что сначала мы проверим, достаточно ли места, и только затем решим, как закодировать данное значение.

Давайте проиллюстрируем эти улучшения на примере рендеринга CSV-таблицы из Data.ByteString.Builder. Его «горячий код» — рендеринг ячеек таблицы, который мы реализуем следующим образом, используя только функции из Builder API.

import Data.ByteString.Builder as B

renderCell :: Cell -> Builder
renderCell (StringC cs) = renderString cs
renderCell (IntC i)     = B.intDec i

renderString :: String -> Builder
renderString cs = B.charUtf8 '"' <> foldMap escape cs <> B.charUtf8 '"'
  where
    escape '\\' = B.charUtf8 '\\' <> B.charUtf8 '\\'
    escape '\"' = B.charUtf8 '\\' <> B.charUtf8 '\"'
    escape c    = B.charUtf8 c

Эффективное кодирование Int в виде десятичных чисел выполняется intDec. Существует потенциал оптимизации для экранирования String . В приведенной выше реализации есть две возможности оптимизации. Во-первых, проверки на отсутствие буфера Builder для экранирования двойных кавычек и обратных слэшей могут быть объединены. Во-вторых, конкатенации, выполняемые foldMap , могут быть устранены. Следующая реализация использует эти оптимизации.

import qualified Data.ByteString.Builder.Prim  as P
import           Data.ByteString.Builder.Prim
                 ( condB, liftFixedToBounded, (>*<), (>$<) )

renderString :: String -> Builder
renderString cs =
    B.charUtf8 '"' <> E.encodeListWithB escape cs <> B.charUtf8 '"'
  where
    escape :: E.BoundedPrim Char
    escape =
      condB (== '\\') (fixed2 ('\\', '\\')) $
      condB (== '\"') (fixed2 ('\\', '\"')) $
      E.charUtf8
     
    {-# INLINE fixed2 #-}
    fixed2 x = liftFixedToBounded $ const x >$< E.char7 >*< E.char7

Код должен быть в основном самодокументирующимся. Несколько неудобный синтаксис обусловлен тем, что комбинаторы написаны так, что размер/предел размера результирующего BoundedPrim может быть вычислен во время компиляции. Мы также явно встраиваем примитив fixed2 , который кодирует фиксированную кортеж символов, чтобы гарантировать, что вычисление предела происходит во время компиляции. При кодировании следующего списка String оптимизированная реализация renderString работает вдвое быстрее.

maxiStrings :: [String]
maxiStrings = take 1000 $ cycle ["hello", "\"1\"", "λ-wörld"]

Большая часть прироста производительности происходит от использования primMapListBounded, который кодирует список значений слева направо с BoundedPrim. Он использует внутренности Builder , чтобы избежать ненужных композиций функций (т.е. конкатенаций). В будущем мы можем ожидать, что компилятор будет выполнять оптимизации, реализованные в primMapListBounded. Однако, кажется, что код слишком сложен для компилятора, чтобы его просмотреть. Поэтому мы предоставляем BoundedPrim escape hatch, который позволяет структурам данных предоставлять очень эффективные проходы кодирования, такие как primMapListBounded для списков.

Обратите внимание, что BoundedPrim несколько громоздки, но довольно универсальны. Вот пример BoundedPrim для комбинированного экранирования HTML и UTF-8 кодирования. Он использует тот факт, что экранированный символ с максимальным кодом Юникода — '>'.

{-# INLINE charUtf8HtmlEscaped #-}
charUtf8HtmlEscaped :: E.BoundedPrim Char
charUtf8HtmlEscaped =
    condB (>  '>' ) E.charUtf8 $
    condB (== '<' ) (fixed4 ('&',('l',('t',';')))) $        -- &lt;
    condB (== '>' ) (fixed4 ('&',('g',('t',';')))) $        -- &gt;
    condB (== '&' ) (fixed5 ('&',('a',('m',('p',';'))))) $  -- &amp;
    condB (== '"' ) (fixed5 ('&',('#',('3',('4',';'))))) $  -- &#34;
    condB (== '\'') (fixed5 ('&',('#',('3',('9',';'))))) $  -- &#39;
    (liftFixedToBounded E.char7)         -- fallback for Chars smaller than '>'
  where
    {-# INLINE fixed4 #-}
    fixed4 x = liftFixedToBounded $ const x >$<
      E.char7 >*< E.char7 >*< E.char7 >*< E.char7
     
    {-# INLINE fixed5 #-}
    fixed5 x = liftFixedToBounded $ const x >$<
      E.char7 >*< E.char7 >*< E.char7 >*< E.char7 >*< E.char7

В настоящее время этот модуль не предоставляет функции, требующие специальных свойств примитивов фиксированного размера. Они полезны для добавления к Builder их размера или для реализации кодирования с блоками. В будущих выпусках этой библиотеки мы предоставим соответствующие функции.

Примитивы ограниченного размера

data BoundedPrim a Source

Примитив билдера, который всегда приводит к последовательности байтов, длина которой не превышает предопределённого предела.

Комбинаторы

Комбинаторы для BoundedPrim реализованы таким образом, что размер результирующего BoundedPrim можно вычислить во время компиляции.

emptyB :: BoundedPrim a Source

BoundedPrim , который всегда приводит к последовательности нулевой длины.

(>*<) :: Monoidal f => f a -> f b -> f (a, b) infixr 5 Source

Оператор пар/конкатенации для примитивов билдера, как ограниченных, так и фиксированного размера.

Например,

toLazyByteString (primFixed (char7 >*< char7) ('x','y')) = "xy"

Мы можем комбинировать несколько примитивов, используя >*< многократно.

toLazyByteString (primFixed (char7 >*< char7 >*< char7) ('x',('y','z'))) = "xyz"

(>$<) :: Contravariant f => (b -> a) -> f a -> f b infixl 4 Source

Оператор, подобный fmap, для примитивов билдера, как ограниченных, так и фиксированного размера.

Примитивы билдера являются контравариантными, поэтому он похож на обычный fmap, но наоборот (посмотрите на тип). (Если это поможет запомнить, символ оператора похож на ($), но наоборот.)

Мы можем использовать его, например, для добавления и/или добавления фиксированных значений к примитиву.

showEncoding ((\x -> ('\'', (x, '\''))) >$< fixed3) 'x' = "'x'"
  where
    fixed3 = char7 >*< char7 >*< char7

Обратите внимание, что несколько громоздкий синтаксис для композиции обусловлен необходимостью вычисления размера/предела размера во время компиляции.

eitherB :: BoundedPrim a -> BoundedPrim b -> BoundedPrim (Either a b) Source

Кодирование значения Either с использованием первого BoundedPrim для значений Left и второго BoundedPrim для значений Right.

Обратите внимание, что функции eitherB, pairB, и contramapB (записанные ниже с использованием >$<) достаточно для построения BoundedPrim для всех нерекурсивных алгебраических типов данных. Например,

maybeB :: BoundedPrim () -> BoundedPrim a -> BoundedPrim (Maybe a)
maybeB nothing just = maybe (Left ()) Right >$< eitherB nothing just
 

condB :: (a -> Bool) -> BoundedPrim a -> BoundedPrim a -> BoundedPrim a Source

Условный выбор BoundedPrim. Например, мы можем реализовать примитив ASCII, который отбрасывает символы с кодами Юникода выше 127, следующим образом.

charASCIIDrop = condB (< '\128') (fromF char7) emptyB
 

Построение билдера

primBounded :: BoundedPrim a -> a -> Builder Source

Создать Builder, который кодирует значения с заданным BoundedPrim.

Мы переписываем последовательные использования primBounded, чтобы проверки границ были объединены. Например,

primBounded (word32 c1) `mappend` primBounded (word32 c2)

переписывается так, что результирующая Builder проверяет только один раз, если есть 8 свободных байтов, вместо проверки дважды, если есть 4 свободных байта. Эта оптимизация не эквивалентна в строгом смысле, поскольку она влияет на границы сгенерированных фрагментов. Однако для пользователя этой библиотеки она эквивалентна, поскольку границы фрагментов ленивого ByteString могут быть наблюдаемы только через внутренний интерфейс. Более того, ожидается, что все примитивы записывают гораздо меньше, чем 4 КБ (стандартный размер короткого буфера). Следовательно, безопасно игнорировать дополнительную потраченную память из-за более агрессивного переключения буфера, введенного этой оптимизацией.

primMapListBounded :: BoundedPrim a -> [a] -> Builder Source

Создать Builder, который кодирует список значений последовательно, используя BoundedPrim для каждого элемента. Эта функция более эффективна, чем каноническая

filter p =
 B.toLazyByteString .
 E.encodeLazyByteStringWithF (E.ifF p E.word8) E.emptyF)
mconcat . map (primBounded w)

или

foldMap (primBounded w)

потому что она перемещает несколько переменных из внутреннего цикла.

primUnfoldrBounded :: BoundedPrim b -> (a -> Maybe (b, a)) -> a -> Builder Source

Создать Builder, который кодирует последовательность, сгенерированную из начального значения, используя BoundedPrim для каждого элемента последовательности.

primMapByteStringBounded :: BoundedPrim Word8 -> ByteString -> Builder Source

Создать Builder, который кодирует каждый Word8 строгого ByteString с помощью BoundedPrim. Например, мы можем написать Builder, который фильтрует строгий ByteString следующим образом.

import Data.ByteString.Builder.Primas P (word8, condB, emptyB)
filterBS p = P.condB p P.word8 P.emptyB

primMapLazyByteStringBounded :: BoundedPrim Word8 -> ByteString -> Builder Source

Применяет primMapByteStringBounded по частям.

Примитивы фиксированного размера

data FixedPrim a Source

Примитив билдера, который всегда приводит к последовательности байтов предопределенного фиксированного размера.

Комбинаторы

Комбинаторы для FixedPrim реализованы таким образом, что размер size результата FixedPrim вычисляется во время компиляции.

Операторы пар и отображения '(>*и '($<)' могут использоваться с FixedPrim.

emptyF :: FixedPrim a Source

FixedPrim, который всегда приводит к последовательности нулевой длины.

liftFixedToBounded :: FixedPrim a -> BoundedPrim a Source

Поднимает FixedPrim к BoundedPrim.

Построение билдера

С точки зрения выразительности, функция fixedPrim была бы достаточной для построения Builder из FixedPrim. Предоставлены объединенные варианты этой функции, так как они позволяют реализовывать её более эффективно. Наши компиляторы пока недостаточно умны; и для некоторых используемых оптимизаций (см. код encodeByteStringWithF) они, скорее всего, никогда ими и не будут.

Обратите внимание, что функции, помеченные «Большой встраиванием.», вынуждены быть встроенными, потому что они должны быть специализированными для конкретных кодировок, но довольно громоздкими по размеру кода. Рекомендуется определять функцию верхнего уровня для каждой конкретной инстанциации такой функции, чтобы разделить её код. Типичным примером является функция byteStringHex из Data.ByteString.Builder.ASCII, которая реализуется следующим образом.

byteStringHex :: S.ByteString -> Builder
byteStringHex = encodeByteStringWithF word8HexFixed

primFixed :: FixedPrim a -> a -> Builder Source

Закодировать значение с FixedPrim.

primMapListFixed :: FixedPrim a -> [a] -> Builder Source

Закодировать список значений слева направо с FixedPrim.

primUnfoldrFixed :: FixedPrim b -> (a -> Maybe (b, a)) -> a -> Builder Source

Закодировать список значений, представленных как unfoldr с FixedPrim.

primMapByteStringFixed :: FixedPrim Word8 -> ByteString -> Builder Source

Большое встраивание. Закодировать все байты строгого ByteString слева направо с FixedPrim. Эта функция достаточно универсальна. Например, мы можем использовать её для построения Builder которая отображает каждый байт перед его копированием в буфер для заполнения.

mapToBuilder :: (Word8 -> Word8) -> S.ByteString -> Builder
mapToBuilder f = encodeByteStringWithF (contramapF f word8)

Мы также можем использовать её для шестнадцатеричного кодирования строгого ByteString как показано в примере byteStringHex выше.

primMapLazyByteStringFixed :: FixedPrim Word8 -> ByteString -> Builder Source

Большое встраивание. Закодировать все байты ленивого ByteString слева направо с FixedPrim.

Стандартные кодировки значений Haskell

Бинарные кодировки

int8 :: FixedPrim Int8 Source

Кодирование отдельных знаковых байтов как есть.

word8 :: FixedPrim Word8 Source

Кодирование отдельных беззнаковых байтов как есть.

Big-endian

int16BE :: FixedPrim Int16 Source

Кодирование Int16 в формате big endian.

int32BE :: FixedPrim Int32 Source

Кодирование Int32 в формате big endian.

int64BE :: FixedPrim Int64 Source

Кодирование Int64 в формате big endian.

word16BE :: FixedPrim Word16 Source

Кодирование Word16 в формате big endian.

word32BE :: FixedPrim Word32 Source

Кодирование Word32 в формате big endian.

word64BE :: FixedPrim Word64 Source

Кодирование Word64 в формате big endian.

floatBE :: FixedPrim Float Source

Кодирование Float в формате big endian.

doubleBE :: FixedPrim Double Source

Кодирование Double в формате big endian.

Little-endian

int16LE :: FixedPrim Int16 Source

Кодирование Int16 в формате little endian.

int32LE :: FixedPrim Int32 Source

Кодирование Int32 в формате little endian.

int64LE :: FixedPrim Int64 Source

Кодирование Int64 в формате little endian.

word16LE :: FixedPrim Word16 Source

Кодирование Word16 в формате little endian.

word32LE :: FixedPrim Word32 Source

Кодирование Word32 в формате little endian.

word64LE :: FixedPrim Word64 Source

Кодирование Word64 в формате little endian.

floatLE :: FixedPrim Float Source

Кодирование Float в формате little endian.

doubleLE :: FixedPrim Double Source

Кодирование Double в формате little endian.

Непортабельное, зависящее от хоста

intHost :: FixedPrim Int Source

Кодирование одного машинного значения Int. Int кодируется в порядке и формате, зависящем от хоста. На 64-битной машине Int представляет собой 8-байтовое значение, на 32-битной машине — 4 байта. Значения, закодированные таким образом, не являются переносимыми на машины с другой разрядностью или порядком байтов без преобразования.

int16Host :: FixedPrim Int16 Source

Кодирование Int16 в порядке и формате, зависящем от хоста.

int32Host :: FixedPrim Int32 Source

Кодирование Int32 в порядке и формате, зависящем от хоста.

int64Host :: FixedPrim Int64 Source

Кодирование Int64 в порядке и формате, зависящем от хоста.

wordHost :: FixedPrim Word Source

Кодирование одного машинного значения Word. Word кодируется в порядке и формате, зависящем от хоста. На 64-битной машине Word представляет собой 8-байтовое значение, на 32-битной машине — 4 байта. Значения, закодированные таким образом, не являются переносимыми на машины с другой разрядностью или порядком байтов без преобразования.

word16Host :: FixedPrim Word16 Source

Кодирование Word16 в порядке и формате, зависящем от хоста.

word32Host :: FixedPrim Word32 Source

Кодирование Word32 в порядке и формате, зависящем от хоста.

word64Host :: FixedPrim Word64 Source

Кодирование Word64 в порядке и формате, зависящем от хоста.

floatHost :: FixedPrim Float Source

Кодирование Float в порядке и формате, зависящем от хоста. Значения, записанные таким образом, не являются переносимыми на машины с другим порядком байтов без преобразования.

doubleHost :: FixedPrim Double Source

Кодирование Double в порядке и формате, зависящем от хоста.

Кодировки символов

ASCII

char7 :: FixedPrim Char Source

Кодирование младших 7 бит Char с использованием кодировки ASCII.

Десятичные числа

Десятичное кодирование чисел с использованием символов, закодированных в ASCII.

int8Dec :: BoundedPrim Int8 Источник

Десятичное кодирование Int8.

int16Dec :: BoundedPrim Int16 Источник

Десятичное кодирование Int16.

int32Dec :: BoundedPrim Int32 Источник

Десятичное кодирование Int32.

int64Dec :: BoundedPrim Int64 Источник

Десятичное кодирование Int64.

intDec :: BoundedPrim Int Источник

Десятичное кодирование Int.

word8Dec :: BoundedPrim Word8 Источник

Десятичное кодирование Word8.

word16Dec :: BoundedPrim Word16 Источник

Десятичное кодирование Word16.

word32Dec :: BoundedPrim Word32 Источник

Десятичное кодирование Word32.

word64Dec :: BoundedPrim Word64 Источник

Десятичное кодирование Word64.

wordDec :: BoundedPrim Word Источник

Десятичное кодирование Word.

Шестнадцатеричные числа

Кодирование целых положительных чисел как шестнадцатеричных чисел с использованием строчных символов ASCII. Используется самое короткое возможное представление. Например,

toLazyByteString (primBounded word16Hex 0x0a10) = "a10"

Обратите внимание, что нет поддержки использования прописных символов. Пожалуйста, свяжитесь с разработчиком, если ваше приложение не может работать без шестнадцатеричного кодирования, использующего прописные символы.

word8Hex :: BoundedPrim Word8 Источник

Шестнадцатеричное кодирование Word8.

word16Hex :: BoundedPrim Word16 Источник

Шестнадцатеричное кодирование Word16.

word32Hex :: BoundedPrim Word32 Источник

Шестнадцатеричное кодирование Word32.

word64Hex :: BoundedPrim Word64 Источник

Шестнадцатеричное кодирование Word64.

wordHex :: BoundedPrim Word Источник

Шестнадцатеричное кодирование Word.

Шестнадцатеричные числа фиксированной ширины

Кодирование байтов типов фиксированной ширины как шестнадцатеричных чисел с использованием строчных символов ASCII. Например,

toLazyByteString (primFixed word16HexFixed 0x0a10) = "0a10"

int8HexFixed :: FixedPrim Int8 Источник

Кодировать Int8, используя 2 тетрады (шестнадцатеричные цифры).

int16HexFixed :: FixedPrim Int16 Источник

Кодировать Int16, используя 4 тетрады.

int32HexFixed :: FixedPrim Int32 Источник

Кодировать Int32, используя 8 тетрад.

int64HexFixed :: FixedPrim Int64 Источник

Кодировать Int64, используя 16 тетрад.

word8HexFixed :: FixedPrim Word8 Источник

Кодировать Word8, используя 2 тетрады (шестнадцатеричные цифры).

word16HexFixed :: FixedPrim Word16 Источник

Кодировать Word16, используя 4 тетрады.

word32HexFixed :: FixedPrim Word32 Источник

Кодировать Word32, используя 8 тетрад.

word64HexFixed :: FixedPrim Word64 Источник

Кодировать Word64, используя 16 тетрад.

floatHexFixed :: FixedPrim Float Источник

Кодировать IEEE Float, используя 8 тетрад.

doubleHexFixed :: FixedPrim Double Источник

Кодировать IEEE Double, используя 16 тетрад.

ISO/IEC 8859-1 (Char8)

Кодировка ISO/IEC 8859-1 — это 8-битовая кодировка, часто известная как Latin-1. Кодировка Char8, реализованная здесь, работает путем усечения кода Юникода до 8 бит и кодирования их как одного байта. Для кодовых точек 0-255 это соответствует кодировке ISO/IEC 8859-1. Обратите внимание, что кодировка Char8 эквивалентна кодировке ASCII для кодовых точек 0-127. Следовательно, такие функции, как intDec , также могут использоваться для кодирования Int как десятичного числа с символами, закодированными в Char8.

char8 :: FixedPrim Символ Источник

Кодирует символ Char.

UTF-8

Кодировка UTF-8 способна кодировать все символы Юникода. Она эквивалентна кодировке ASCII для символов с кодами 0-127. Следовательно, функции, такие как intDec, также могут использоваться для кодирования Int в виде десятичного числа с символами, закодированными в UTF-8.

charUtf8 :: BoundedPrim Символ Источник

Кодирует символ Char.

© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/7.10.3/docs/html/libraries/bytestring-0.10.6.0/Data-ByteString-Builder-Prim.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API