16 Compact byte strings. |
16 Compact byte strings. |
17 |
17 |
18 See \<^file>\<open>$ISABELLE_HOME/src/Pure/General/bytes.ML\<close> |
18 See \<^file>\<open>$ISABELLE_HOME/src/Pure/General/bytes.ML\<close> |
19 and \<^file>\<open>$ISABELLE_HOME/src/Pure/General/bytes.scala\<close>. |
19 and \<^file>\<open>$ISABELLE_HOME/src/Pure/General/bytes.scala\<close>. |
20 -} |
20 -} |
|
21 |
|
22 {-# LANGUAGE TypeApplications #-} |
21 |
23 |
22 module Isabelle.Bytes ( |
24 module Isabelle.Bytes ( |
23 Bytes, |
25 Bytes, |
24 make, unmake, pack, unpack, |
26 make, unmake, pack, unpack, |
25 empty, null, length, index, all, any, |
27 empty, null, length, index, all, any, |
26 head, last, take, drop, isPrefixOf, isSuffixOf, |
28 head, last, take, drop, isPrefixOf, isSuffixOf, |
27 concat, space, spaces, singleton, char, byte, |
29 concat, space, spaces, char, byte, max_byte, max_char, singleton, |
28 trim_line |
30 trim_line |
29 ) |
31 ) |
30 where |
32 where |
31 |
33 |
32 import Prelude hiding (null, length, all, any, head, last, take, drop, concat) |
34 import Prelude hiding (null, length, all, any, head, last, take, drop, concat) |
106 spaces :: Int -> Bytes |
108 spaces :: Int -> Bytes |
107 spaces n = |
109 spaces n = |
108 if n < 64 then small_spaces ! n |
110 if n < 64 then small_spaces ! n |
109 else concat ((small_spaces ! (n `mod` 64)) : replicate (n `div` 64) (small_spaces ! 64)) |
111 else concat ((small_spaces ! (n `mod` 64)) : replicate (n `div` 64) (small_spaces ! 64)) |
110 |
112 |
|
113 char :: Word8 -> Char |
|
114 char = toEnum . fromEnum |
|
115 |
|
116 byte :: Char -> Word8 |
|
117 byte = toEnum . fromEnum |
|
118 |
|
119 max_byte :: Word8 |
|
120 max_byte = maxBound |
|
121 |
|
122 max_char :: Char |
|
123 max_char = char max_byte |
|
124 |
111 singletons :: Array Word8 Bytes |
125 singletons :: Array Word8 Bytes |
112 singletons = |
126 singletons = |
113 array (minBound, maxBound) |
127 array (0, max_byte) |
114 [(i, make (ByteString.singleton i)) | i <- [minBound .. maxBound]] |
128 [(i, make (ByteString.singleton i)) | i <- [0 .. max_byte]] |
115 |
129 |
116 singleton :: Word8 -> Bytes |
130 singleton :: Word8 -> Bytes |
117 singleton b = singletons ! b |
131 singleton b = singletons ! b |
118 |
|
119 char :: Word8 -> Char |
|
120 char = toEnum . fromEnum |
|
121 |
|
122 byte :: Char -> Word8 |
|
123 byte = toEnum . fromEnum |
|
124 |
132 |
125 trim_line :: Bytes -> Bytes |
133 trim_line :: Bytes -> Bytes |
126 trim_line s = |
134 trim_line s = |
127 if n >= 2 && at (n - 2) == '\r' && at (n - 1) == '\n' then take (n - 2) s |
135 if n >= 2 && at (n - 2) == '\r' && at (n - 1) == '\n' then take (n - 2) s |
128 else if n >= 1 && (at (n - 1) == '\r' || at (n - 1) == '\n') then take (n - 1) s |
136 else if n >= 1 && (at (n - 1) == '\r' || at (n - 1) == '\n') then take (n - 1) s |
304 separate _ xs = xs; |
312 separate _ xs = xs; |
305 |
313 |
306 |
314 |
307 {- string-like interfaces -} |
315 {- string-like interfaces -} |
308 |
316 |
309 class (IsList a, IsString a, Monoid a, Eq a, Ord a) => StringLike a |
317 class (IsString a, Monoid a, Eq a, Ord a) => StringLike a where |
310 where |
318 space_explode :: Char -> a -> [a] |
311 space_explode :: Item a -> a -> [a] |
|
312 split_lines :: a -> [a] |
|
313 |
319 |
314 instance StringLike String where |
320 instance StringLike String where |
315 space_explode sep = Split.split (Split.dropDelims (Split.whenElt (== sep))) |
321 space_explode c = Split.split (Split.dropDelims (Split.whenElt (== c))) |
316 split_lines = space_explode '\n' |
|
317 |
322 |
318 instance StringLike Text where |
323 instance StringLike Text where |
319 space_explode sep str = |
324 space_explode c str = |
320 if Text.null str then [] |
325 if Text.null str then [] |
321 else if Text.all (/= sep) str then [str] |
326 else if Text.all (/= c) str then [str] |
322 else map Text.pack $ space_explode sep $ Text.unpack str |
327 else map Text.pack $ space_explode c $ Text.unpack str |
323 split_lines = space_explode '\n' |
|
324 |
328 |
325 instance StringLike Lazy.Text where |
329 instance StringLike Lazy.Text where |
326 space_explode sep str = |
330 space_explode c str = |
327 if Lazy.null str then [] |
331 if Lazy.null str then [] |
328 else if Lazy.all (/= sep) str then [str] |
332 else if Lazy.all (/= c) str then [str] |
329 else map Lazy.pack $ space_explode sep $ Lazy.unpack str |
333 else map Lazy.pack $ space_explode c $ Lazy.unpack str |
330 split_lines = space_explode '\n' |
|
331 |
334 |
332 instance StringLike Bytes where |
335 instance StringLike Bytes where |
333 space_explode sep str = |
336 space_explode c str = |
334 if Bytes.null str then [] |
337 if Bytes.null str then [] |
335 else if Bytes.all (/= sep) str then [str] |
338 else if c > Bytes.max_char || Bytes.all (/= (Bytes.byte c)) str then [str] |
336 else explode (Bytes.unpack str) |
339 else |
337 where |
340 explode (Bytes.unpack str) |
338 explode rest = |
341 where |
339 case span (/= sep) rest of |
342 explode rest = |
340 (_, []) -> [Bytes.pack rest] |
343 case span (/= (Bytes.byte c)) rest of |
341 (prfx, _ : rest') -> Bytes.pack prfx : explode rest' |
344 (_, []) -> [Bytes.pack rest] |
342 split_lines = space_explode (Bytes.byte '\n') |
345 (prfx, _ : rest') -> Bytes.pack prfx : explode rest' |
343 |
346 |
344 class StringLike a => STRING a where make_string :: a -> String |
347 class StringLike a => STRING a where make_string :: a -> String |
345 instance STRING String where make_string = id |
348 instance STRING String where make_string = id |
346 instance STRING Text where make_string = Text.unpack |
349 instance STRING Text where make_string = Text.unpack |
347 instance STRING Lazy.Text where make_string = Lazy.unpack |
350 instance STRING Lazy.Text where make_string = Lazy.unpack |
378 space_implode s = mconcat . separate s |
381 space_implode s = mconcat . separate s |
379 |
382 |
380 commas, commas_quote :: StringLike a => [a] -> a |
383 commas, commas_quote :: StringLike a => [a] -> a |
381 commas = space_implode ", " |
384 commas = space_implode ", " |
382 commas_quote = commas . map quote |
385 commas_quote = commas . map quote |
|
386 |
|
387 split_lines :: StringLike a => a -> [a] |
|
388 split_lines = space_explode '\n' |
383 |
389 |
384 cat_lines :: StringLike a => [a] -> a |
390 cat_lines :: StringLike a => [a] -> a |
385 cat_lines = space_implode "\n" |
391 cat_lines = space_implode "\n" |
386 |
392 |
387 trim_line :: String -> String |
393 trim_line :: String -> String |
1726 keyword1, keyword2 :: BYTES a => a -> T |
1732 keyword1, keyword2 :: BYTES a => a -> T |
1727 keyword1 name = mark_str (Markup.keyword1, name) |
1733 keyword1 name = mark_str (Markup.keyword1, name) |
1728 keyword2 name = mark_str (Markup.keyword2, name) |
1734 keyword2 name = mark_str (Markup.keyword2, name) |
1729 |
1735 |
1730 text :: BYTES a => a -> [T] |
1736 text :: BYTES a => a -> [T] |
1731 text = breaks . map str . filter (not . Bytes.null) . space_explode Bytes.space . make_bytes |
1737 text = breaks . map str . filter (not . Bytes.null) . space_explode ' ' . make_bytes |
1732 |
1738 |
1733 paragraph :: [T] -> T |
1739 paragraph :: [T] -> T |
1734 paragraph = markup Markup.paragraph |
1740 paragraph = markup Markup.paragraph |
1735 |
1741 |
1736 para :: BYTES a => a -> T |
1742 para :: BYTES a => a -> T |
2477 write_message socket = write socket . make_message |
2483 write_message socket = write socket . make_message |
2478 |
2484 |
2479 parse_header :: Bytes -> [Int] |
2485 parse_header :: Bytes -> [Int] |
2480 parse_header line = |
2486 parse_header line = |
2481 let |
2487 let |
2482 res = map Value.parse_nat (space_explode (Bytes.byte ',') line) |
2488 res = map Value.parse_nat (space_explode ',' line) |
2483 in |
2489 in |
2484 if all isJust res then map fromJust res |
2490 if all isJust res then map fromJust res |
2485 else error ("Malformed message header: " <> quote (UTF8.decode line)) |
2491 else error ("Malformed message header: " <> quote (UTF8.decode line)) |
2486 |
2492 |
2487 read_chunk :: Socket -> Int -> IO Bytes |
2493 read_chunk :: Socket -> Int -> IO Bytes |