1 USING: accessors arrays assocs bit-arrays http2.hpack
2 io.encodings.string io.encodings.utf8 kernel literals locals
5 IN: http2.hpack.huffman
8 ! Table contents from RFC 7541 Appendix B
9 CONSTANT: huffman-table {
269 :: R2, ( n -- ) n , n 2 64 * + , n 1 64 * + , n 3 64 * + , ;
270 :: R4, ( n -- ) n R2, n 2 16 * + R2, n 1 16 * + R2, n 3 16 * + R2, ;
271 :: R6, ( n -- ) n R4, n 2 4 * + R4, n 1 4 * + R4, n 3 4 * + R4, ;
274 ! The codes for each entry in the huffman table
275 CONSTANT: huffman-encode-table $[
277 [ integer>bit-array ] dip f pad-tail reverse
283 CONSTANT: bit-reverse-table $[
284 [ 0 R6, 2 R6, 1 R6, 3 R6, ] B{ } make
287 : reverse-bits ( byte-array -- byte-array' )
288 [ bit-reverse-table nth ] B{ } map-as ;
290 : byte-array>bit-array ( byte-array -- bit-array )
291 [ length 8 * ] [ bit-array boa ] bi ;
293 ! converts a byte array/vector/sequence to a bit array, with
294 ! each byte in descending order, such that the most significant
295 ! bit of the first byte is the first bit in the sequence.
296 : bytes-to-bits ( bytes -- bits )
297 reverse-bits byte-array>bit-array ;
299 ! most significant bit first.
300 : bits-to-bytes ( bits -- bytes )
301 underlying>> reverse-bits ;
303 ERROR: hpack-huffman-error message ;
305 ! probably inefficient, but it works.
306 ! just loops over the bits, adding each bit to the current code and searching for
307 ! the current code, adding the corresponding symbol if the code
308 ! is found in the table.
309 :: huffman-decode ( bytes -- string )
310 BV{ } clone :> byte-vector
311 0 0 ! current code and length
312 bytes bytes-to-bits [
313 [ 2 * ] 2dip 1 0 ? swap 1 [ + ] 2bi@
314 2dup 2array huffman-table index
316 dup EOS = [ "End of Stream in huffman encoded string" hpack-huffman-error ] when
317 byte-vector push 2drop 0 0
321 7 > [ "Padding is too long in huffman encoded string" hpack-huffman-error ] when
323 EOS huffman-table nth first integer>bit-array
324 swap integer>bit-array tail?
325 [ "Padding is not the most significant bits of the End of Stream code in huffman encoded string" hpack-huffman-error ] unless
327 byte-vector utf8 decode ;
329 : huffman-encode ( string -- bytes )
330 [ huffman-encode-table nth ] { } map-as concat
331 EOS huffman-encode-table nth over length neg 8 rem head
332 append bits-to-bytes ;