Data.ByteString.Builder
| Авторские права | (c) 2010 Jasper Van der Jeugt (c) 2010 - 2011 Simon Meier |
|---|---|
| Лицензия | BSD3-стиль (см. LICENSE) |
| Поддержка | Simon Meier <iridcode@gmail.com> |
| Переносимость | GHC |
| Безопасный Haskell | Надёжный |
| Язык | Haskell98 |
Содержание
Описание
Builder используются для эффективного построения последовательностей байтов из более мелких частей. Обычно такое построение является частью реализации кодирования, т. е. функции для преобразования значений Haskell в последовательности байтов. Примерами кодировок являются генерация последовательности байтов, представляющей HTML-документ, который отправляется в HTTP-ответе веб-приложением, или сериализация значения Haskell с использованием фиксированного бинарного формата.
Для эффективной реализации кодирования важно, чтобы (а) затрачивалось мало времени на преобразование значений Haskell в результирующую последовательность байтов и (б) представление результирующей последовательности было таким, чтобы ее можно было эффективно использовать. Builder поддерживают (а), предоставляя операцию конкатенации O(1) и эффективные реализации основных кодировок для Char, Int и других стандартных значений Haskell. Они поддерживают (б), представляя свой результат как ленивую ByteString, которая внутри себя представляет собой просто связанный список указателей на блоки последовательных блоков памяти. Ленивые ByteString могут эффективно использоваться функциями, которые записывают их в файл или отправляют их по сетевому сокету. Обратите внимание, что каждый предел блока влечёт дорогостоящие дополнительные затраты (например, системный вызов), которые необходимо амортизировать за счёт затрат на обработку тела блока. Builder поэтому уделяют особое внимание, чтобы средний размер блока был достаточно большим. Точное значение «достаточно большого» зависит от приложения. Текущая реализация настроена на средний размер блока между 4 КБ и 32 КБ, что должно подойти для большинства приложений.
В качестве простого примера реализации кодирования мы покажем, как эффективно преобразовать следующее представление смешанных таблиц данных в UTF-8 закодированную таблицу значений, разделённых запятыми (CSV).
data Cell = StringC String
| IntC Int
deriving( Eq, Ord, Show )
type Row = [Cell]
type Table = [Row]
Мы используем следующие импорты и сокращаем mappend для упрощения чтения.
import qualified Data.ByteString.Lazy as L import Data.ByteString.Builder import Data.Monoid import Data.Foldable (foldMap) import Data.List (intersperse) infixr 4 <> (<>) ::Monoidm => m -> m -> m (<>) =mappend
CSV — это представление таблиц на основе символов. Для максимальной модульности мы могли бы сначала преобразовать Table в String и затем закодировать это String с помощью какой-либо кодировки символов Юникода. Однако это приведёт к потере производительности из-за того, что промежуточное представление String будет построено и сразу же удалено. Мы избавляемся от этого промежуточного представления String, задав кодировку символов UTF-8 и используя Builder для преобразования Table напрямую в UTF-8 закодированные CSV таблицы, представленные в виде ленивых ByteString.
encodeUtf8CSV :: Table -> L.ByteString encodeUtf8CSV =toLazyByteString. renderTable renderTable :: Table -> Builder renderTable rs =mconcat[renderRow r <>charUtf8'\n' | r <- rs] renderRow :: Row -> Builder renderRow [] =memptyrenderRow (c:cs) = renderCell c <> mconcat [ charUtf8 ',' <> renderCell c' | c' <- cs ] renderCell :: Cell -> Builder renderCell (StringC cs) = renderString cs renderCell (IntC i) =intDeci renderString :: String -> Builder renderString cs = charUtf8 '"' <> foldMap escape cs <> charUtf8 '"' where escape '\\' = charUtf8 '\\' <> charUtf8 '\\' escape '\"' = charUtf8 '\\' <> charUtf8 '\"' escape c = charUtf8 c
Обратите внимание, что кодировка ASCII является подмножеством кодировки UTF-8, поэтому мы можем использовать оптимизированную функцию intDec для кодирования Int как десятичного числа с UTF-8 закодированными цифрами. Использование intDec более эффективно, чем stringUtf8 . show, так как это позволяет избежать создания промежуточного String. Избежание этой промежуточной структуры данных значительно улучшает производительность, поскольку кодирование Cell является основной операцией для отображения таблиц CSV. См. Data.ByteString.Builder.Prim для получения дополнительной информации о том, как улучшить производительность renderString.
Мы демонстрируем нашу функцию кодирования UTF-8 CSV на следующей таблице.
strings :: [String] strings = ["hello", "\"1\"", "λ-wörld"] table :: Table table = [map StringC strings, map IntC [-3..3]]
Выражение encodeUtf8CSV table приводит к следующей ленивой ByteString.
Chunk "\"hello\",\"\\\"1\\\"\",\"\206\187-w\195\182rld\"\n-3,-2,-1,0,1,2,3\n" Empty
Мы можем ясно видеть, что мы преобразуем в бинарный формат. Символы «λ» и «ö», имеющие код Юникода выше 127, расширяются до их соответствующего многобайтового представления UTF-8.
Мы используем библиотеку criterion (http://hackage.haskell.org/package/criterion) для оценки производительности нашей функции кодирования на следующей таблице.
import Criterion.Main -- add this import to the ones above
maxiTable :: Table
maxiTable = take 1000 $ cycle table
main :: IO ()
main = defaultMain
[ bench "encodeUtf8CSV maxiTable (original)" $
whnf (L.length . encodeUtf8CSV) maxiTable
]
На процессоре Core2 Duo 2.20 ГГц на 32-битной Linux вышеприведенный код тратит 1 мс на генерацию ленивой ByteString длиной 22 500 байт. Вновь взглянув на определения выше, мы видим, что мы позаботились об избегании промежуточных структур данных, так как в противном случае мы бы потеряли производительность. Например, следующее (вероятно, более простое) определение renderRow примерно на 20% медленнее.
renderRow :: Row -> Builder renderRow = mconcat . intersperse (charUtf8 ',') . map renderCell
Аналогично, следует избегать использования конкатенаций O(n), таких как ++ или эквивалентных операций concat над строгими и ленивыми ByteString. Следующее определение renderString также примерно на 20% медленнее.
renderString :: String -> Builder
renderString cs = charUtf8 $ "\"" ++ concatMap escape cs ++ "\""
where
escape '\\' = "\\"
escape '\"' = "\\\""
escape c = return c
Помимо удаления промежуточных структур данных, кодировки можно дополнительно оптимизировать, настроив их параметры выполнения с помощью функций в Data.ByteString.Builder.Extra и их «внутренних циклов» с помощью функций в Data.ByteString.Builder.Prim.
Тип Builder
Builder обозначают последовательности байтов. Они являются Monoid, где mempty — это последовательность нулевой длины, а mappend — это конкатенация, которая выполняется за O(1).
Выполнение Builder
Внутри Builder — это функции заполнения буфера. Они выполняются драйвером, который предоставляет им фактический буфер для заполнения. После вызова с буфером, Builder заполняет его и возвращает сигнал драйверу, сообщая, что он либо закончил, заполнил текущий буфер, или хочет напрямую вставить ссылку на блок памяти. В последних двух случаях Builder также возвращает продолжение Builder, которое драйвер может вызвать для заполнения следующего буфера. Здесь мы предоставляем два драйвера, которые удовлетворяют почти всем случаям использования. См. Data.ByteString.Builder.Extra для получения информации о настройке их параметров.
toLazyByteString :: Builder -> ByteString Источник
Выполнить Builder и вернуть сгенерированные блоки как ленивую ByteString. Работа выполняется лениво, т. е. только при принудительном вычислении блока ленивой ByteString.
hPutBuilder :: Handle -> Builder -> IO () Источник
Вывести Builder в Handle. Builder выполняется непосредственно в буфере Handle.
Рекомендуется установить Handle в двоичный и BlockBuffering режим. См. hSetBinaryMode и hSetBuffering.
Эта функция более эффективна, чем hPut . toLazyByteString, так как во многих случаях не требуется выделение буфера. Кроме того, результаты нескольких выполнений коротких Builder объединяются в буфере Handle , тем самым избегая ненужных сбросов буфера.
Создание Builder
Бинарные кодировки
byteString :: ByteString -> Builder Источник
Создать Builder, обозначающую ту же последовательность байтов, что и строгий ByteString. Builder вставляет большие ByteString напрямую, но копирует маленькие, чтобы обеспечить, что сгенерированные блоки в среднем будут большими.
lazyByteString :: ByteString -> Builder Источник
Создать Builder, обозначающую ту же последовательность байтов, что и ленивая ByteString. Builder вставляет большие блоки ленивой ByteString напрямую, но копирует маленькие, чтобы обеспечить, что сгенерированные блоки в среднем будут большими.
shortByteString :: ShortByteString -> Builder Источник
Построить Builder, копирующий ShortByteString.
int8 :: Int8 -> Builder Source
Закодировать один подписанный байт как есть.
word8 :: Word8 -> Builder Source
Закодировать один беззнаковый байт как есть.
Big-endian
int16BE :: Int16 -> Builder Source
Закодировать Int16 в формате big endian.
int32BE :: Int32 -> Builder Source
Закодировать Int32 в формате big endian.
int64BE :: Int64 -> Builder Source
Закодировать Int64 в формате big endian.
word16BE :: Word16 -> Builder Source
Закодировать Word16 в формате big endian.
word32BE :: Word32 -> Builder Source
Закодировать Word32 в формате big endian.
word64BE :: Word64 -> Builder Source
Закодировать Word64 в формате big endian.
floatBE :: Float -> Builder Source
Закодировать Float в формате big endian.
doubleBE :: Double -> Builder Source
Закодировать Double в формате big endian.
Little-endian
int16LE :: Int16 -> Builder Source
Закодировать Int16 в формате little endian.
int32LE :: Int32 -> Builder Source
Закодировать Int32 в формате little endian.
int64LE :: Int64 -> Builder Source
Закодировать Int64 в формате little endian.
word16LE :: Word16 -> Builder Source
Закодировать Word16 в формате little endian.
word32LE :: Word32 -> Builder Source
Закодировать Word32 в формате little endian.
word64LE :: Word64 -> Builder Source
Закодировать Word64 в формате little endian.
floatLE :: Float -> Builder Source
Закодировать Float в формате little endian.
doubleLE :: Double -> Builder Source
Закодировать Double в формате little endian.
Кодировки символов
Преобразование из Char и String в Builder в различных кодировках.
ASCII (Char7)
Кодировка ASCII является 7-битной кодировкой. Реализованная здесь кодировка Char7 работает путем усечения кодовой точки Unicode до 7 бит, добавления перед ней ведущего 0 и кодирования полученных 8 бит в один байт. Для кодовых точек 0-127 это соответствует кодировке ASCII.
char7 :: Char -> Builder Source
Char7 кодирует Char.
string7 :: String -> Builder Source
Char7 кодирует String.
ISO/IEC 8859-1 (Char8)
Кодировка ISO/IEC 8859-1 является 8-битной кодировкой, часто известной как Latin-1. Реализованная здесь кодировка Char8 работает путем усечения кодовой точки Unicode до 8 бит и кодирования их в один байт. Для кодовых точек 0-255 это соответствует кодировке ISO/IEC 8859-1.
char8 :: Char -> Builder Source
Char8 кодирует Char.
string8 :: String -> Builder Source
Char8 кодирует String.
UTF-8
Кодировка UTF-8 может кодировать все кодовые точки Unicode. Мы рекомендуем всегда использовать её для кодирования Char и String, если приложение действительно не требует другой кодировки.
charUtf8 :: Char -> Builder Source
UTF-8 кодирует Char.
stringUtf8 :: String -> Builder Source
UTF-8 кодирует String.
Форматирование чисел как текст
Форматирование чисел как ASCII-текст.
Обратите внимание, что вы также можете использовать эти функции для кодировок ISO/IEC 8859-1 и UTF-8, поскольку кодировка ASCII эквивалентна для кодовых точек 0-127.
Десятичные числа
Десятичное кодирование чисел с использованием ASCII-кодированных символов.
int8Dec :: Int8 -> Builder Source
Десятичное кодирование Int8 с использованием десятичных цифр ASCII.
Например:
toLazyByteString (int8Dec 42) = "42" toLazyByteString (int8Dec (-1)) = "-1"
int16Dec :: Int16 -> Builder Source
Десятичное кодирование Int16 с использованием десятичных цифр ASCII.
int32Dec :: Int32 -> Builder Source
Десятичное кодирование Int32 с использованием ASCII-цифр.
int64Dec :: Int64 -> Builder Source
Десятичное кодирование Int64 с использованием ASCII-цифр.
intDec :: Int -> Builder Source
Десятичное кодирование Int с использованием ASCII-цифр.
integerDec :: Integer -> Builder Source
Десятичное кодирование Integer с использованием ASCII-цифр.
word8Dec :: Word8 -> Builder Source
Десятичное кодирование Word8 с использованием ASCII-цифр.
word16Dec :: Word16 -> Builder Source
Десятичное кодирование Word16 с использованием ASCII-цифр.
word32Dec :: Word32 -> Builder Source
Десятичное кодирование Word32 с использованием ASCII-цифр.
word64Dec :: Word64 -> Builder Source
Десятичное кодирование Word64 с использованием ASCII-цифр.
wordDec :: Word -> Builder Source
Десятичное кодирование Word с использованием ASCII-цифр.
floatDec :: Float -> Builder Source
Сейчас медленно. Десятичное кодирование IEEE Float.
doubleDec :: Double -> Builder Source
Сейчас медленно. Десятичное кодирование IEEE Double.
Шестнадцатеричные числа
Кодирование целых положительных чисел в шестнадцатеричном формате с использованием строчных ASCII-символов. Используется кратчайшее возможное представление. Например,
>>>toLazyByteString (word16Hex 0x0a10)Chunk "a10" Empty
Обратите внимание, что поддержка использования прописных символов отсутствует. Пожалуйста, обратитесь к разработчику, если ваше приложение не может работать без шестнадцатеричных кодировок, использующих прописные символы.
word8Hex :: Word8 -> Builder Source
Кратчайшее шестнадцатеричное кодирование Word8 с использованием строчных символов.
word16Hex :: Word16 -> Builder Source
Кратчайшее шестнадцатеричное кодирование Word16 с использованием строчных символов.
word32Hex :: Word32 -> Builder Source
Кратчайшее шестнадцатеричное кодирование Word32 с использованием строчных символов.
word64Hex :: Word64 -> Builder Source
Кратчайшее шестнадцатеричное кодирование Word64 с использованием строчных символов.
wordHex :: Word -> Builder Source
Кратчайшее шестнадцатеричное кодирование Word с использованием строчных символов.
Шестнадцатеричные числа фиксированной ширины
int8HexFixed :: Int8 -> Builder Source
Кодирование Int8 с использованием 2 тетрад (шестнадцатеричных цифр).
int16HexFixed :: Int16 -> Builder Source
Кодирование Int16 с использованием 4 тетрад.
int32HexFixed :: Int32 -> Builder Source
Кодирование Int32 с использованием 8 тетрад.
int64HexFixed :: Int64 -> Builder Source
Кодирование Int64 с использованием 16 тетрад.
word8HexFixed :: Word8 -> Builder Source
Кодирование Word8 с использованием 2 тетрад (шестнадцатеричных цифр).
word16HexFixed :: Word16 -> Builder Source
Кодирование Word16 с использованием 4 тетрад.
word32HexFixed :: Word32 -> Builder Source
Кодирование Word32 с использованием 8 тетрад.
word64HexFixed :: Word64 -> Builder Source
Кодирование Word64 с использованием 16 тетрад.
floatHexFixed :: Float -> Builder Source
Кодирование IEEE Float с использованием 8 тетрад.
doubleHexFixed :: Double -> Builder Source
Кодирование IEEE Double с использованием 16 тетрад.
byteStringHex :: ByteString -> Builder Source
Кодирует каждый байт ByteString с использованием его фиксированной ширины шестнадцатеричного кодирования.
lazyByteStringHex :: ByteString -> Builder Source
Кодирует каждый байт ленивого ByteString с использованием его фиксированной ширины шестнадцатеричного кодирования.
© 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.html