3c99b42015-10-14Martin Nilsson /* -*- c -*- || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
1a71352015-07-10Henrik Grubbström (Grubba) /*! @module HPack *!
5322fa2015-08-24Henrik Grubbström (Grubba)  *! Implementation of the HPACK (@rfc{7541@}) header packing standard.
1a71352015-07-10Henrik Grubbström (Grubba)  *!
5322fa2015-08-24Henrik Grubbström (Grubba)  *! This is the header packing system that is used in HTTP/2 (@rfc{7540@}).
1a71352015-07-10Henrik Grubbström (Grubba)  */ #include "module.h"
14008d2018-01-18Martin Nilsson #include "interpret.h"
1a71352015-07-10Henrik Grubbström (Grubba)  #include "huffman-tab.h" DECLARATIONS; #undef ASSERT #ifdef PIKE_DEBUG
45bfc92015-07-28Henrik Grubbström (Grubba) #define ASSERT(X) do { \ if (!(X)) { \ Pike_fatal("%s:%d: Assertion failed: %s.\n", \ __FILE__, __LINE__, TOSTR(X)); \ } \
1a71352015-07-10Henrik Grubbström (Grubba)  } while(0) #else #define ASSERT(X) 0 #endif /*! @decl string(8bit) huffman_encode(string(8bit) str) *! *! Encodes the string @[str] with the static huffman code specified
5322fa2015-08-24Henrik Grubbström (Grubba)  *! in @rfc{7541:B@}.
1a71352015-07-10Henrik Grubbström (Grubba)  *! *! @param str *! String to encode. *! *! @returns *! Returns the encoded string. *! *! @seealso *! @[huffman_decode()]. */ PIKEFUN string(8bit) huffman_encode(string(8bit) str) { unsigned char *inbytes = STR0(str); unsigned char *outbytes; unsigned INT32 huffbuf = 0; unsigned INT32 huffbits = 0; size_t total_bits = 0; struct pike_string *res; ptrdiff_t i; for (i = str->len; i--;) { total_bits += pack_tab[inbytes[i]].bits; } res = begin_shared_string((total_bits + 7)>>3); outbytes = STR0(res); for (i = 0; i < str->len; i++, inbytes++) { const struct huffentry *entry = &pack_tab[*inbytes]; huffbuf |= entry->code >> huffbits; huffbits += entry->bits; if (huffbits > 7) { if (UNLIKELY(huffbits > 32)) { /* Buffer overflow. Maximum number of needed bits is 37. */ *outbytes = huffbuf>>24; huffbuf <<= 8; huffbits -= 8; outbytes++;
4eb8272015-07-10Henrik Grubbström (Grubba)  if (huffbits >= entry->bits) { huffbuf |= entry->code >> (huffbits - entry->bits); } else { huffbuf |= entry->code << (entry->bits - huffbits); }
1a71352015-07-10Henrik Grubbström (Grubba)  } while (huffbits > 7) { *outbytes = huffbuf>>24; huffbuf <<= 8; huffbits -= 8; outbytes++; } } } if (huffbits) { /* Pad with the most significant bits of EOS (ie ~0). */ *outbytes = (huffbuf >> 24) | (0xff >> (huffbits & 7)); outbytes++; } ASSERT(outbytes == (STR0(res) + res->len)); pop_stack(); push_string(end_shared_string(res)); }
e335e12015-07-12Henrik Grubbström (Grubba) static const struct huffentry *find_huffentry(unsigned INT32 huffbuf) { const struct huffentry *entry;
c77e722015-07-13Henrik Grubbström (Grubba)  unsigned int code = (huffbuf >> 24) & 0xff; int low = unpack_index[code]; int high = unpack_index[code+1]; while (1) {
e335e12015-07-12Henrik Grubbström (Grubba)  int m = (low + high)>>1;
c77e722015-07-13Henrik Grubbström (Grubba)  if (m == low) break;
e335e12015-07-12Henrik Grubbström (Grubba)  entry = &unpack_tab[m]; if (entry->code > huffbuf) high = m; else low = m;
c77e722015-07-13Henrik Grubbström (Grubba)  }
f9d6b62015-07-16Henrik Grubbström (Grubba)  if (UNLIKELY(low == 255) && UNLIKELY(huffbuf & 4)) { /* Invalid encoding. EOS in stream. */ return NULL; }
e335e12015-07-12Henrik Grubbström (Grubba)  return &unpack_tab[low]; }
1a71352015-07-10Henrik Grubbström (Grubba) /*! @decl string(8bit) huffman_decode(string(8bit) str) *! *! Decodes the string @[str] encoded with the static huffman code specified
5322fa2015-08-24Henrik Grubbström (Grubba)  *! in @rfc{7541:B@}.
1a71352015-07-10Henrik Grubbström (Grubba)  *! *! @param str *! String to decode. *! *! @returns *! Returns the decoded string. *! *! @seealso *! @[huffman_encode()]. */ PIKEFUN string(8bit) huffman_decode(string(8bit) str) { unsigned char *inbytes = STR0(str); struct string_builder out;
45bfc92015-07-28Henrik Grubbström (Grubba)  unsigned INT_TYPE huffbuf = 0;
1a71352015-07-10Henrik Grubbström (Grubba)  unsigned INT32 huffbits = 0; ptrdiff_t i; init_string_builder(&out, 0); for (i = 0; i < str->len; i++, inbytes++) {
45bfc92015-07-28Henrik Grubbström (Grubba)  unsigned INT_TYPE c = *inbytes;
1a71352015-07-10Henrik Grubbström (Grubba)  huffbits += 8;
45bfc92015-07-28Henrik Grubbström (Grubba)  huffbuf |= c << ((sizeof(huffbuf)<<3) - huffbits); if (huffbits > ((sizeof(huffbuf)-1)<<3)) {
77ca372015-07-14Henrik Grubbström (Grubba)  /* Try to extract some characters. */
1a71352015-07-10Henrik Grubbström (Grubba)  while(1) {
45bfc92015-07-28Henrik Grubbström (Grubba)  unsigned INT32 huffkey = huffbuf >> ((sizeof(huffbuf)-4)<<3); const struct huffentry *entry = find_huffentry(huffkey);
f9d6b62015-07-16Henrik Grubbström (Grubba)  if (UNLIKELY(!entry) || (entry->bits > huffbits)) {
1a71352015-07-10Henrik Grubbström (Grubba)  /* More bits needed. */ break; }
45bfc92015-07-28Henrik Grubbström (Grubba)  ASSERT(entry->code == (huffkey & ~((1<<(32 - entry->bits))-1)));
1a71352015-07-10Henrik Grubbström (Grubba)  string_builder_putchar(&out, entry->sym); huffbuf <<= entry->bits; huffbits -= entry->bits;
f061912015-07-12Henrik Grubbström (Grubba)  if (huffbits < 5) break;
1a71352015-07-10Henrik Grubbström (Grubba)  }
77ca372015-07-14Henrik Grubbström (Grubba) 
45bfc92015-07-28Henrik Grubbström (Grubba) #if (SIZEOF_INT_TYPE < 5) if (UNLIKELY(huffbits > 24)) {
77ca372015-07-14Henrik Grubbström (Grubba)  /* We need to read more bits than will fit in huffbuf. */ const struct huffentry *entry; unsigned INT32 lostbits = huffbits - 24; inbytes++; i++;
45bfc92015-07-28Henrik Grubbström (Grubba)  if (UNLIKELY(i >= str->len)) break;
77ca372015-07-14Henrik Grubbström (Grubba)  c = *inbytes; huffbuf |= c >> lostbits; huffbits = 32; entry = find_huffentry(huffbuf);
f9d6b62015-07-16Henrik Grubbström (Grubba)  if (UNLIKELY(!entry)) break;
77ca372015-07-14Henrik Grubbström (Grubba)  ASSERT(entry->code == (huffbuf & ~((1<<(32 - entry->bits))-1))); string_builder_putchar(&out, entry->sym); huffbits -= entry->bits - lostbits; huffbuf = c << (32 - huffbits); }
45bfc92015-07-28Henrik Grubbström (Grubba) #endif /* SIZEOF_INT_TYPE < 5 */
1a71352015-07-10Henrik Grubbström (Grubba)  } } while (huffbits) {
45bfc92015-07-28Henrik Grubbström (Grubba)  unsigned INT32 huffkey;
1a71352015-07-10Henrik Grubbström (Grubba)  const struct huffentry *entry;
45bfc92015-07-28Henrik Grubbström (Grubba)  huffbuf |= ((unsigned INT_TYPE)~0) >> huffbits; if (huffbuf == (unsigned INT_TYPE)~0) {
1a71352015-07-10Henrik Grubbström (Grubba)  /* EOS */ huffbits = 0; break; }
f061912015-07-12Henrik Grubbström (Grubba)  if (huffbits < 5) break;
45bfc92015-07-28Henrik Grubbström (Grubba)  huffkey = huffbuf >> ((sizeof(huffbuf)-4)<<3); entry = find_huffentry(huffkey);
f9d6b62015-07-16Henrik Grubbström (Grubba)  if (UNLIKELY(!entry) || (entry->bits > huffbits)) {
1a71352015-07-10Henrik Grubbström (Grubba)  /* BAD */ break; }
45bfc92015-07-28Henrik Grubbström (Grubba)  ASSERT(entry->code == (huffkey & ~((1<<(32 - entry->bits))-1)));
1a71352015-07-10Henrik Grubbström (Grubba)  string_builder_putchar(&out, entry->sym); huffbuf <<= entry->bits; huffbits -= entry->bits; } if (huffbits) { /* Invalid encoding. */ free_string_builder(&out); Pike_error("Invalid huffman encoding.\n"); } pop_stack(); push_string(finish_string_builder(&out)); } /*! @endmodule */ PIKE_MODULE_INIT { INIT; } PIKE_MODULE_EXIT { EXIT; }