/*
 * nghttp2 - HTTP/2 C Library
 *
 * Copyright (c) 2012 Tatsuhiro Tsujikawa
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "nghttp2_helper.h"

#include <assert.h>
#include <string.h>

#include "nghttp2_net.h"

void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) {
  uint16_t x = htons(n);
  memcpy(buf, &x, sizeof(uint16_t));
}

void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) {
  uint32_t x = htonl(n);
  memcpy(buf, &x, sizeof(uint32_t));
}

uint16_t nghttp2_get_uint16(const uint8_t *data) {
  uint16_t n;
  memcpy(&n, data, sizeof(uint16_t));
  return ntohs(n);
}

uint32_t nghttp2_get_uint32(const uint8_t *data) {
  uint32_t n;
  memcpy(&n, data, sizeof(uint32_t));
  return ntohl(n);
}

/* Generated by gendowncasetbl.py */
const uint8_t nghttp2_downcase_tbl[] = {
  0x00 /* NUL  */, 0x01 /* SOH  */, 0x02 /* STX  */, 0x03 /* ETX  */,
  0x04 /* EOT  */, 0x05 /* ENQ  */, 0x06 /* ACK  */, 0x07 /* BEL  */,
  0x08 /* BS   */, 0x09 /* HT   */, 0x0A /* LF   */, 0x0B /* VT   */,
  0x0C /* FF   */, 0x0D /* CR   */, 0x0E /* SO   */, 0x0F /* SI   */,
  0x10 /* DLE  */, 0x11 /* DC1  */, 0x12 /* DC2  */, 0x13 /* DC3  */,
  0x14 /* DC4  */, 0x15 /* NAK  */, 0x16 /* SYN  */, 0x17 /* ETB  */,
  0x18 /* CAN  */, 0x19 /* EM   */, 0x1A /* SUB  */, 0x1B /* ESC  */,
  0x1C /* FS   */, 0x1D /* GS   */, 0x1E /* RS   */, 0x1F /* US   */,
  0x20 /* SPC  */, 0x21 /* !    */, 0x22 /* "    */, 0x23 /* #    */,
  0x24 /* $    */, 0x25 /* %    */, 0x26 /* &    */, 0x27 /* '    */,
  0x28 /* (    */, 0x29 /* )    */, 0x2A /* *    */, 0x2B /* +    */,
  0x2C /* ,    */, 0x2D /* -    */, 0x2E /* .    */, 0x2F /* /    */,
  0x30 /* 0    */, 0x31 /* 1    */, 0x32 /* 2    */, 0x33 /* 3    */,
  0x34 /* 4    */, 0x35 /* 5    */, 0x36 /* 6    */, 0x37 /* 7    */,
  0x38 /* 8    */, 0x39 /* 9    */, 0x3A /* :    */, 0x3B /* ;    */,
  0x3C /* <    */, 0x3D /* =    */, 0x3E /* >    */, 0x3F /* ?    */,
  0x40 /* @    */, 0x61 /* A    */, 0x62 /* B    */, 0x63 /* C    */,
  0x64 /* D    */, 0x65 /* E    */, 0x66 /* F    */, 0x67 /* G    */,
  0x68 /* H    */, 0x69 /* I    */, 0x6A /* J    */, 0x6B /* K    */,
  0x6C /* L    */, 0x6D /* M    */, 0x6E /* N    */, 0x6F /* O    */,
  0x70 /* P    */, 0x71 /* Q    */, 0x72 /* R    */, 0x73 /* S    */,
  0x74 /* T    */, 0x75 /* U    */, 0x76 /* V    */, 0x77 /* W    */,
  0x78 /* X    */, 0x79 /* Y    */, 0x7A /* Z    */, 0x5B /* [    */,
  0x5C /* \    */, 0x5D /* ]    */, 0x5E /* ^    */, 0x5F /* _    */,
  0x60 /* `    */, 0x61 /* a    */, 0x62 /* b    */, 0x63 /* c    */,
  0x64 /* d    */, 0x65 /* e    */, 0x66 /* f    */, 0x67 /* g    */,
  0x68 /* h    */, 0x69 /* i    */, 0x6A /* j    */, 0x6B /* k    */,
  0x6C /* l    */, 0x6D /* m    */, 0x6E /* n    */, 0x6F /* o    */,
  0x70 /* p    */, 0x71 /* q    */, 0x72 /* r    */, 0x73 /* s    */,
  0x74 /* t    */, 0x75 /* u    */, 0x76 /* v    */, 0x77 /* w    */,
  0x78 /* x    */, 0x79 /* y    */, 0x7A /* z    */, 0x7B /* {    */,
  0x7C /* |    */, 0x7D /* }    */, 0x7E /* ~    */, 0x7F /* DEL  */,
  0x80 /* 0x80 */, 0x81 /* 0x81 */, 0x82 /* 0x82 */, 0x83 /* 0x83 */,
  0x84 /* 0x84 */, 0x85 /* 0x85 */, 0x86 /* 0x86 */, 0x87 /* 0x87 */,
  0x88 /* 0x88 */, 0x89 /* 0x89 */, 0x8A /* 0x8A */, 0x8B /* 0x8B */,
  0x8C /* 0x8C */, 0x8D /* 0x8D */, 0x8E /* 0x8E */, 0x8F /* 0x8F */,
  0x90 /* 0x90 */, 0x91 /* 0x91 */, 0x92 /* 0x92 */, 0x93 /* 0x93 */,
  0x94 /* 0x94 */, 0x95 /* 0x95 */, 0x96 /* 0x96 */, 0x97 /* 0x97 */,
  0x98 /* 0x98 */, 0x99 /* 0x99 */, 0x9A /* 0x9A */, 0x9B /* 0x9B */,
  0x9C /* 0x9C */, 0x9D /* 0x9D */, 0x9E /* 0x9E */, 0x9F /* 0x9F */,
  0xA0 /* 0xA0 */, 0xA1 /* 0xA1 */, 0xA2 /* 0xA2 */, 0xA3 /* 0xA3 */,
  0xA4 /* 0xA4 */, 0xA5 /* 0xA5 */, 0xA6 /* 0xA6 */, 0xA7 /* 0xA7 */,
  0xA8 /* 0xA8 */, 0xA9 /* 0xA9 */, 0xAA /* 0xAA */, 0xAB /* 0xAB */,
  0xAC /* 0xAC */, 0xAD /* 0xAD */, 0xAE /* 0xAE */, 0xAF /* 0xAF */,
  0xB0 /* 0xB0 */, 0xB1 /* 0xB1 */, 0xB2 /* 0xB2 */, 0xB3 /* 0xB3 */,
  0xB4 /* 0xB4 */, 0xB5 /* 0xB5 */, 0xB6 /* 0xB6 */, 0xB7 /* 0xB7 */,
  0xB8 /* 0xB8 */, 0xB9 /* 0xB9 */, 0xBA /* 0xBA */, 0xBB /* 0xBB */,
  0xBC /* 0xBC */, 0xBD /* 0xBD */, 0xBE /* 0xBE */, 0xBF /* 0xBF */,
  0xC0 /* 0xC0 */, 0xC1 /* 0xC1 */, 0xC2 /* 0xC2 */, 0xC3 /* 0xC3 */,
  0xC4 /* 0xC4 */, 0xC5 /* 0xC5 */, 0xC6 /* 0xC6 */, 0xC7 /* 0xC7 */,
  0xC8 /* 0xC8 */, 0xC9 /* 0xC9 */, 0xCA /* 0xCA */, 0xCB /* 0xCB */,
  0xCC /* 0xCC */, 0xCD /* 0xCD */, 0xCE /* 0xCE */, 0xCF /* 0xCF */,
  0xD0 /* 0xD0 */, 0xD1 /* 0xD1 */, 0xD2 /* 0xD2 */, 0xD3 /* 0xD3 */,
  0xD4 /* 0xD4 */, 0xD5 /* 0xD5 */, 0xD6 /* 0xD6 */, 0xD7 /* 0xD7 */,
  0xD8 /* 0xD8 */, 0xD9 /* 0xD9 */, 0xDA /* 0xDA */, 0xDB /* 0xDB */,
  0xDC /* 0xDC */, 0xDD /* 0xDD */, 0xDE /* 0xDE */, 0xDF /* 0xDF */,
  0xE0 /* 0xE0 */, 0xE1 /* 0xE1 */, 0xE2 /* 0xE2 */, 0xE3 /* 0xE3 */,
  0xE4 /* 0xE4 */, 0xE5 /* 0xE5 */, 0xE6 /* 0xE6 */, 0xE7 /* 0xE7 */,
  0xE8 /* 0xE8 */, 0xE9 /* 0xE9 */, 0xEA /* 0xEA */, 0xEB /* 0xEB */,
  0xEC /* 0xEC */, 0xED /* 0xED */, 0xEE /* 0xEE */, 0xEF /* 0xEF */,
  0xF0 /* 0xF0 */, 0xF1 /* 0xF1 */, 0xF2 /* 0xF2 */, 0xF3 /* 0xF3 */,
  0xF4 /* 0xF4 */, 0xF5 /* 0xF5 */, 0xF6 /* 0xF6 */, 0xF7 /* 0xF7 */,
  0xF8 /* 0xF8 */, 0xF9 /* 0xF9 */, 0xFA /* 0xFA */, 0xFB /* 0xFB */,
  0xFC /* 0xFC */, 0xFD /* 0xFD */, 0xFE /* 0xFE */, 0xFF /* 0xFF */,
};

void nghttp2_downcase(uint8_t *s, size_t len) {
  size_t i;
  for (i = 0; i < len; ++i) {
    s[i] = nghttp2_downcase_byte(s[i]);
  }
}

/*
 *   local_window_size
 *   ^  *
 *   |  *    recv_window_size
 *   |  *  * ^
 *   |  *  * |
 *  0+++++++++
 *   |  *  *   \
 *   |  *  *   | This rage is hidden in flow control.  But it must be
 *   v  *  *   / kept in order to restore it when window size is enlarged.
 *   recv_reduction
 *   (+ for negative direction)
 *
 *   recv_window_size could be negative if we decrease
 *   local_window_size more than recv_window_size:
 *
 *   local_window_size
 *   ^  *
 *   |  *
 *   |  *
 *   0++++++++
 *   |  *    ^ recv_window_size (negative)
 *   |  *    |
 *   v  *  *
 *   recv_reduction
 */
int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
                                     int32_t *recv_window_size_ptr,
                                     int32_t *recv_reduction_ptr,
                                     int32_t *delta_ptr) {
  if (*delta_ptr > 0) {
    int32_t recv_reduction_delta;
    int32_t delta;
    int32_t new_recv_window_size =
      nghttp2_max_int32(0, *recv_window_size_ptr) - *delta_ptr;

    if (new_recv_window_size >= 0) {
      *recv_window_size_ptr = new_recv_window_size;
      return 0;
    }

    delta = -new_recv_window_size;

    /* The delta size is strictly more than received bytes. Increase
       local_window_size by that difference |delta|. */
    if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
      return NGHTTP2_ERR_FLOW_CONTROL;
    }
    *local_window_size_ptr += delta;
    /* If there is recv_reduction due to earlier window_size
       reduction, we have to adjust it too. */
    recv_reduction_delta = nghttp2_min_int32(*recv_reduction_ptr, delta);
    *recv_reduction_ptr -= recv_reduction_delta;
    if (*recv_window_size_ptr < 0) {
      *recv_window_size_ptr += recv_reduction_delta;
    } else {
      /* If *recv_window_size_ptr > 0, then those bytes are going to
         be returned to the remote peer (by WINDOW_UPDATE with the
         adjusted *delta_ptr), so it is effectively 0 now.  We set to
         *recv_reduction_delta, because caller does not take into
         account it in *delta_ptr. */
      *recv_window_size_ptr = recv_reduction_delta;
    }
    /* recv_reduction_delta must be paid from *delta_ptr, since it was
       added in window size reduction (see below). */
    *delta_ptr -= recv_reduction_delta;

    return 0;
  }

  if (*local_window_size_ptr + *delta_ptr < 0 ||
      *recv_window_size_ptr < INT32_MIN - *delta_ptr ||
      *recv_reduction_ptr > INT32_MAX + *delta_ptr) {
    return NGHTTP2_ERR_FLOW_CONTROL;
  }
  /* Decreasing local window size. Note that we achieve this without
     noticing to the remote peer. To do this, we cut
     recv_window_size by -delta. This means that we don't send
     WINDOW_UPDATE for -delta bytes. */
  *local_window_size_ptr += *delta_ptr;
  *recv_window_size_ptr += *delta_ptr;
  *recv_reduction_ptr -= *delta_ptr;
  *delta_ptr = 0;

  return 0;
}

int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr,
                                       int32_t *recv_window_size_ptr,
                                       int32_t *recv_reduction_ptr,
                                       int32_t *delta_ptr) {
  int32_t recv_reduction_delta;
  int32_t delta;

  delta = *delta_ptr;

  assert(delta >= 0);

  /* The delta size is strictly more than received bytes. Increase
     local_window_size by that difference |delta|. */
  if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
    return NGHTTP2_ERR_FLOW_CONTROL;
  }

  *local_window_size_ptr += delta;
  /* If there is recv_reduction due to earlier window_size
     reduction, we have to adjust it too. */
  recv_reduction_delta = nghttp2_min_int32(*recv_reduction_ptr, delta);
  *recv_reduction_ptr -= recv_reduction_delta;

  *recv_window_size_ptr += recv_reduction_delta;

  /* recv_reduction_delta must be paid from *delta_ptr, since it was
     added in window size reduction (see below). */
  *delta_ptr -= recv_reduction_delta;

  return 0;
}

int nghttp2_should_send_window_update(int32_t local_window_size,
                                      int32_t recv_window_size) {
  return recv_window_size > 0 && recv_window_size >= local_window_size / 2;
}

const char *nghttp2_strerror(int error_code) {
  switch (error_code) {
  case 0:
    return "Success";
  case NGHTTP2_ERR_INVALID_ARGUMENT:
    return "Invalid argument";
  case NGHTTP2_ERR_BUFFER_ERROR:
    return "Out of buffer space";
  case NGHTTP2_ERR_UNSUPPORTED_VERSION:
    return "Unsupported SPDY version";
  case NGHTTP2_ERR_WOULDBLOCK:
    return "Operation would block";
  case NGHTTP2_ERR_PROTO:
    return "Protocol error";
  case NGHTTP2_ERR_INVALID_FRAME:
    return "Invalid frame octets";
  case NGHTTP2_ERR_EOF:
    return "EOF";
  case NGHTTP2_ERR_DEFERRED:
    return "Data transfer deferred";
  case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
    return "No more Stream ID available";
  case NGHTTP2_ERR_STREAM_CLOSED:
    return "Stream was already closed or invalid";
  case NGHTTP2_ERR_STREAM_CLOSING:
    return "Stream is closing";
  case NGHTTP2_ERR_STREAM_SHUT_WR:
    return "The transmission is not allowed for this stream";
  case NGHTTP2_ERR_INVALID_STREAM_ID:
    return "Stream ID is invalid";
  case NGHTTP2_ERR_INVALID_STREAM_STATE:
    return "Invalid stream state";
  case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
    return "Another DATA frame has already been deferred";
  case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
    return "request HEADERS is not allowed";
  case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:
    return "GOAWAY has already been sent";
  case NGHTTP2_ERR_INVALID_HEADER_BLOCK:
    return "Invalid header block";
  case NGHTTP2_ERR_INVALID_STATE:
    return "Invalid state";
  case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:
    return "The user callback function failed due to the temporal error";
  case NGHTTP2_ERR_FRAME_SIZE_ERROR:
    return "The length of the frame is invalid";
  case NGHTTP2_ERR_HEADER_COMP:
    return "Header compression/decompression error";
  case NGHTTP2_ERR_FLOW_CONTROL:
    return "Flow control error";
  case NGHTTP2_ERR_INSUFF_BUFSIZE:
    return "Insufficient buffer size given to function";
  case NGHTTP2_ERR_PAUSE:
    return "Callback was paused by the application";
  case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS:
    return "Too many inflight SETTINGS";
  case NGHTTP2_ERR_PUSH_DISABLED:
    return "Server push is disabled by peer";
  case NGHTTP2_ERR_DATA_EXIST:
    return "DATA or HEADERS frame has already been submitted for the stream";
  case NGHTTP2_ERR_SESSION_CLOSING:
    return "The current session is closing";
  case NGHTTP2_ERR_HTTP_HEADER:
    return "Invalid HTTP header field was received";
  case NGHTTP2_ERR_HTTP_MESSAGING:
    return "Violation in HTTP messaging rule";
  case NGHTTP2_ERR_REFUSED_STREAM:
    return "Stream was refused";
  case NGHTTP2_ERR_INTERNAL:
    return "Internal error";
  case NGHTTP2_ERR_CANCEL:
    return "Cancel";
  case NGHTTP2_ERR_SETTINGS_EXPECTED:
    return "When a local endpoint expects to receive SETTINGS frame, it "
           "receives an other type of frame";
  case NGHTTP2_ERR_NOMEM:
    return "Out of memory";
  case NGHTTP2_ERR_CALLBACK_FAILURE:
    return "The user callback function failed";
  case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
    return "Received bad client magic byte string";
  case NGHTTP2_ERR_FLOODED:
    return "Flooding was detected in this HTTP/2 session, and it must be "
           "closed";
  case NGHTTP2_ERR_TOO_MANY_SETTINGS:
    return "SETTINGS frame contained more than the maximum allowed entries";
  case NGHTTP2_ERR_TOO_MANY_CONTINUATIONS:
    return "Too many CONTINUATION frames following a HEADER frame";
  default:
    return "Unknown error code";
  }
}

static const uint8_t VALID_HD_NAME_CHARS[256] = {
  ['!'] = 1, ['#'] = 1, ['$'] = 1, ['%'] = 1, ['&'] = 1, ['\''] = 1, ['*'] = 1,
  ['+'] = 1, ['-'] = 1, ['.'] = 1, ['0'] = 1, ['1'] = 1, ['2'] = 1,  ['3'] = 1,
  ['4'] = 1, ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,  ['A'] = 2,
  ['B'] = 2, ['C'] = 2, ['D'] = 2, ['E'] = 2, ['F'] = 2, ['G'] = 2,  ['H'] = 2,
  ['I'] = 2, ['J'] = 2, ['K'] = 2, ['L'] = 2, ['M'] = 2, ['N'] = 2,  ['O'] = 2,
  ['P'] = 2, ['Q'] = 2, ['R'] = 2, ['S'] = 2, ['T'] = 2, ['U'] = 2,  ['V'] = 2,
  ['W'] = 2, ['X'] = 2, ['Y'] = 2, ['Z'] = 2, ['^'] = 1, ['_'] = 1,  ['`'] = 1,
  ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,  ['g'] = 1,
  ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1,  ['n'] = 1,
  ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1,  ['u'] = 1,
  ['v'] = 1, ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1, ['|'] = 1,  ['~'] = 1,
};

int nghttp2_check_header_name(const uint8_t *name, size_t len) {
  const uint8_t *last;
  if (len == 0) {
    return 0;
  }
  if (*name == ':') {
    if (len == 1) {
      return 0;
    }
    ++name;
    --len;
  }
  for (last = name + len; name != last; ++name) {
    if (VALID_HD_NAME_CHARS[*name] != 1) {
      return 0;
    }
  }
  return 1;
}

int nghttp2_check_nonempty_header_name(const uint8_t *name, size_t len) {
  const uint8_t *last;
  int rv;

  for (last = name + len; name != last; ++name) {
    rv = VALID_HD_NAME_CHARS[*name];
    if (rv != 1) {
      return rv;
    }
  }

  return 1;
}

static const uint8_t VALID_HD_VALUE_CHARS[256] = {
  ['\t'] = 1, [' '] = 1,  ['!'] = 1,  ['"'] = 1,  ['#'] = 1,  ['$'] = 1,
  ['%'] = 1,  ['&'] = 1,  ['\''] = 1, ['('] = 1,  [')'] = 1,  ['*'] = 1,
  ['+'] = 1,  [','] = 1,  ['-'] = 1,  ['.'] = 1,  ['/'] = 1,  ['0'] = 1,
  ['1'] = 1,  ['2'] = 1,  ['3'] = 1,  ['4'] = 1,  ['5'] = 1,  ['6'] = 1,
  ['7'] = 1,  ['8'] = 1,  ['9'] = 1,  [':'] = 1,  [';'] = 1,  ['<'] = 1,
  ['='] = 1,  ['>'] = 1,  ['?'] = 1,  ['@'] = 1,  ['A'] = 1,  ['B'] = 1,
  ['C'] = 1,  ['D'] = 1,  ['E'] = 1,  ['F'] = 1,  ['G'] = 1,  ['H'] = 1,
  ['I'] = 1,  ['J'] = 1,  ['K'] = 1,  ['L'] = 1,  ['M'] = 1,  ['N'] = 1,
  ['O'] = 1,  ['P'] = 1,  ['Q'] = 1,  ['R'] = 1,  ['S'] = 1,  ['T'] = 1,
  ['U'] = 1,  ['V'] = 1,  ['W'] = 1,  ['X'] = 1,  ['Y'] = 1,  ['Z'] = 1,
  ['['] = 1,  ['\\'] = 1, [']'] = 1,  ['^'] = 1,  ['_'] = 1,  ['`'] = 1,
  ['a'] = 1,  ['b'] = 1,  ['c'] = 1,  ['d'] = 1,  ['e'] = 1,  ['f'] = 1,
  ['g'] = 1,  ['h'] = 1,  ['i'] = 1,  ['j'] = 1,  ['k'] = 1,  ['l'] = 1,
  ['m'] = 1,  ['n'] = 1,  ['o'] = 1,  ['p'] = 1,  ['q'] = 1,  ['r'] = 1,
  ['s'] = 1,  ['t'] = 1,  ['u'] = 1,  ['v'] = 1,  ['w'] = 1,  ['x'] = 1,
  ['y'] = 1,  ['z'] = 1,  ['{'] = 1,  ['|'] = 1,  ['}'] = 1,  ['~'] = 1,
  [0x80] = 1, [0x81] = 1, [0x82] = 1, [0x83] = 1, [0x84] = 1, [0x85] = 1,
  [0x86] = 1, [0x87] = 1, [0x88] = 1, [0x89] = 1, [0x8A] = 1, [0x8B] = 1,
  [0x8C] = 1, [0x8D] = 1, [0x8E] = 1, [0x8F] = 1, [0x90] = 1, [0x91] = 1,
  [0x92] = 1, [0x93] = 1, [0x94] = 1, [0x95] = 1, [0x96] = 1, [0x97] = 1,
  [0x98] = 1, [0x99] = 1, [0x9A] = 1, [0x9B] = 1, [0x9C] = 1, [0x9D] = 1,
  [0x9E] = 1, [0x9F] = 1, [0xA0] = 1, [0xA1] = 1, [0xA2] = 1, [0xA3] = 1,
  [0xA4] = 1, [0xA5] = 1, [0xA6] = 1, [0xA7] = 1, [0xA8] = 1, [0xA9] = 1,
  [0xAA] = 1, [0xAB] = 1, [0xAC] = 1, [0xAD] = 1, [0xAE] = 1, [0xAF] = 1,
  [0xB0] = 1, [0xB1] = 1, [0xB2] = 1, [0xB3] = 1, [0xB4] = 1, [0xB5] = 1,
  [0xB6] = 1, [0xB7] = 1, [0xB8] = 1, [0xB9] = 1, [0xBA] = 1, [0xBB] = 1,
  [0xBC] = 1, [0xBD] = 1, [0xBE] = 1, [0xBF] = 1, [0xC0] = 1, [0xC1] = 1,
  [0xC2] = 1, [0xC3] = 1, [0xC4] = 1, [0xC5] = 1, [0xC6] = 1, [0xC7] = 1,
  [0xC8] = 1, [0xC9] = 1, [0xCA] = 1, [0xCB] = 1, [0xCC] = 1, [0xCD] = 1,
  [0xCE] = 1, [0xCF] = 1, [0xD0] = 1, [0xD1] = 1, [0xD2] = 1, [0xD3] = 1,
  [0xD4] = 1, [0xD5] = 1, [0xD6] = 1, [0xD7] = 1, [0xD8] = 1, [0xD9] = 1,
  [0xDA] = 1, [0xDB] = 1, [0xDC] = 1, [0xDD] = 1, [0xDE] = 1, [0xDF] = 1,
  [0xE0] = 1, [0xE1] = 1, [0xE2] = 1, [0xE3] = 1, [0xE4] = 1, [0xE5] = 1,
  [0xE6] = 1, [0xE7] = 1, [0xE8] = 1, [0xE9] = 1, [0xEA] = 1, [0xEB] = 1,
  [0xEC] = 1, [0xED] = 1, [0xEE] = 1, [0xEF] = 1, [0xF0] = 1, [0xF1] = 1,
  [0xF2] = 1, [0xF3] = 1, [0xF4] = 1, [0xF5] = 1, [0xF6] = 1, [0xF7] = 1,
  [0xF8] = 1, [0xF9] = 1, [0xFA] = 1, [0xFB] = 1, [0xFC] = 1, [0xFD] = 1,
  [0xFE] = 1, [0xFF] = 1,
};

int nghttp2_check_header_value(const uint8_t *value, size_t len) {
  const uint8_t *last;
  for (last = value + len; value != last; ++value) {
    if (!VALID_HD_VALUE_CHARS[*value]) {
      return 0;
    }
  }
  return 1;
}

int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len) {
  if (len == 0) {
    return 1;
  }

  if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' ||
      *(value + len - 1) == '\t') {
    return 0;
  }

  return nghttp2_check_header_value(value, len);
}

static const uint8_t VALID_METHOD_CHARS[256] = {
  ['!'] = 1, ['#'] = 1, ['$'] = 1, ['%'] = 1, ['&'] = 1, ['\''] = 1, ['*'] = 1,
  ['+'] = 1, ['-'] = 1, ['.'] = 1, ['0'] = 1, ['1'] = 1, ['2'] = 1,  ['3'] = 1,
  ['4'] = 1, ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,  ['A'] = 1,
  ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1,  ['H'] = 1,
  ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1,  ['O'] = 1,
  ['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1, ['U'] = 1,  ['V'] = 1,
  ['W'] = 1, ['X'] = 1, ['Y'] = 1, ['Z'] = 1, ['^'] = 1, ['_'] = 1,  ['`'] = 1,
  ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,  ['g'] = 1,
  ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1,  ['n'] = 1,
  ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1,  ['u'] = 1,
  ['v'] = 1, ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1, ['|'] = 1,  ['~'] = 1,
};

int nghttp2_check_method(const uint8_t *value, size_t len) {
  const uint8_t *last;
  if (len == 0) {
    return 0;
  }
  for (last = value + len; value != last; ++value) {
    if (!VALID_METHOD_CHARS[*value]) {
      return 0;
    }
  }
  return 1;
}

static const uint8_t VALID_PATH_CHARS[256] = {
  ['!'] = 1,  ['"'] = 1,  ['#'] = 1,  ['$'] = 1,  ['%'] = 1,  ['&'] = 1,
  ['\''] = 1, ['('] = 1,  [')'] = 1,  ['*'] = 1,  ['+'] = 1,  [','] = 1,
  ['-'] = 1,  ['.'] = 1,  ['/'] = 1,  ['0'] = 1,  ['1'] = 1,  ['2'] = 1,
  ['3'] = 1,  ['4'] = 1,  ['5'] = 1,  ['6'] = 1,  ['7'] = 1,  ['8'] = 1,
  ['9'] = 1,  [':'] = 1,  [';'] = 1,  ['<'] = 1,  ['='] = 1,  ['>'] = 1,
  ['?'] = 1,  ['@'] = 1,  ['A'] = 1,  ['B'] = 1,  ['C'] = 1,  ['D'] = 1,
  ['E'] = 1,  ['F'] = 1,  ['G'] = 1,  ['H'] = 1,  ['I'] = 1,  ['J'] = 1,
  ['K'] = 1,  ['L'] = 1,  ['M'] = 1,  ['N'] = 1,  ['O'] = 1,  ['P'] = 1,
  ['Q'] = 1,  ['R'] = 1,  ['S'] = 1,  ['T'] = 1,  ['U'] = 1,  ['V'] = 1,
  ['W'] = 1,  ['X'] = 1,  ['Y'] = 1,  ['Z'] = 1,  ['['] = 1,  ['\\'] = 1,
  [']'] = 1,  ['^'] = 1,  ['_'] = 1,  ['`'] = 1,  ['a'] = 1,  ['b'] = 1,
  ['c'] = 1,  ['d'] = 1,  ['e'] = 1,  ['f'] = 1,  ['g'] = 1,  ['h'] = 1,
  ['i'] = 1,  ['j'] = 1,  ['k'] = 1,  ['l'] = 1,  ['m'] = 1,  ['n'] = 1,
  ['o'] = 1,  ['p'] = 1,  ['q'] = 1,  ['r'] = 1,  ['s'] = 1,  ['t'] = 1,
  ['u'] = 1,  ['v'] = 1,  ['w'] = 1,  ['x'] = 1,  ['y'] = 1,  ['z'] = 1,
  ['{'] = 1,  ['|'] = 1,  ['}'] = 1,  ['~'] = 1,  [0x80] = 1, [0x81] = 1,
  [0x82] = 1, [0x83] = 1, [0x84] = 1, [0x85] = 1, [0x86] = 1, [0x87] = 1,
  [0x88] = 1, [0x89] = 1, [0x8A] = 1, [0x8B] = 1, [0x8C] = 1, [0x8D] = 1,
  [0x8E] = 1, [0x8F] = 1, [0x90] = 1, [0x91] = 1, [0x92] = 1, [0x93] = 1,
  [0x94] = 1, [0x95] = 1, [0x96] = 1, [0x97] = 1, [0x98] = 1, [0x99] = 1,
  [0x9A] = 1, [0x9B] = 1, [0x9C] = 1, [0x9D] = 1, [0x9E] = 1, [0x9F] = 1,
  [0xA0] = 1, [0xA1] = 1, [0xA2] = 1, [0xA3] = 1, [0xA4] = 1, [0xA5] = 1,
  [0xA6] = 1, [0xA7] = 1, [0xA8] = 1, [0xA9] = 1, [0xAA] = 1, [0xAB] = 1,
  [0xAC] = 1, [0xAD] = 1, [0xAE] = 1, [0xAF] = 1, [0xB0] = 1, [0xB1] = 1,
  [0xB2] = 1, [0xB3] = 1, [0xB4] = 1, [0xB5] = 1, [0xB6] = 1, [0xB7] = 1,
  [0xB8] = 1, [0xB9] = 1, [0xBA] = 1, [0xBB] = 1, [0xBC] = 1, [0xBD] = 1,
  [0xBE] = 1, [0xBF] = 1, [0xC0] = 1, [0xC1] = 1, [0xC2] = 1, [0xC3] = 1,
  [0xC4] = 1, [0xC5] = 1, [0xC6] = 1, [0xC7] = 1, [0xC8] = 1, [0xC9] = 1,
  [0xCA] = 1, [0xCB] = 1, [0xCC] = 1, [0xCD] = 1, [0xCE] = 1, [0xCF] = 1,
  [0xD0] = 1, [0xD1] = 1, [0xD2] = 1, [0xD3] = 1, [0xD4] = 1, [0xD5] = 1,
  [0xD6] = 1, [0xD7] = 1, [0xD8] = 1, [0xD9] = 1, [0xDA] = 1, [0xDB] = 1,
  [0xDC] = 1, [0xDD] = 1, [0xDE] = 1, [0xDF] = 1, [0xE0] = 1, [0xE1] = 1,
  [0xE2] = 1, [0xE3] = 1, [0xE4] = 1, [0xE5] = 1, [0xE6] = 1, [0xE7] = 1,
  [0xE8] = 1, [0xE9] = 1, [0xEA] = 1, [0xEB] = 1, [0xEC] = 1, [0xED] = 1,
  [0xEE] = 1, [0xEF] = 1, [0xF0] = 1, [0xF1] = 1, [0xF2] = 1, [0xF3] = 1,
  [0xF4] = 1, [0xF5] = 1, [0xF6] = 1, [0xF7] = 1, [0xF8] = 1, [0xF9] = 1,
  [0xFA] = 1, [0xFB] = 1, [0xFC] = 1, [0xFD] = 1, [0xFE] = 1, [0xFF] = 1,
};

int nghttp2_check_path(const uint8_t *value, size_t len) {
  const uint8_t *last;
  for (last = value + len; value != last; ++value) {
    if (!VALID_PATH_CHARS[*value]) {
      return 0;
    }
  }
  return 1;
}

static const uint8_t VALID_AUTHORITY_CHARS[256] = {
  ['!'] = 1, ['$'] = 1, ['%'] = 1, ['&'] = 1, ['\''] = 1, ['('] = 1, [')'] = 1,
  ['*'] = 1, ['+'] = 1, [','] = 1, ['-'] = 1, ['.'] = 1,  ['0'] = 1, ['1'] = 1,
  ['2'] = 1, ['3'] = 1, ['4'] = 1, ['5'] = 1, ['6'] = 1,  ['7'] = 1, ['8'] = 1,
  ['9'] = 1, [':'] = 1, [';'] = 1, ['='] = 1, ['@'] = 1,  ['A'] = 1, ['B'] = 1,
  ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1,  ['H'] = 1, ['I'] = 1,
  ['J'] = 1, ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1,  ['O'] = 1, ['P'] = 1,
  ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1, ['U'] = 1,  ['V'] = 1, ['W'] = 1,
  ['X'] = 1, ['Y'] = 1, ['Z'] = 1, ['['] = 1, [']'] = 1,  ['_'] = 1, ['a'] = 1,
  ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,  ['g'] = 1, ['h'] = 1,
  ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1,  ['n'] = 1, ['o'] = 1,
  ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1,  ['u'] = 1, ['v'] = 1,
  ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1, ['~'] = 1,
};

int nghttp2_check_authority(const uint8_t *value, size_t len) {
  const uint8_t *last;
  for (last = value + len; value != last; ++value) {
    if (!VALID_AUTHORITY_CHARS[*value]) {
      return 0;
    }
  }
  return 1;
}

uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {
  if (len == 0) {
    return dest;
  }

  memcpy(dest, src, len);

  return dest + len;
}

const char *nghttp2_http2_strerror(uint32_t error_code) {
  switch (error_code) {
  case NGHTTP2_NO_ERROR:
    return "NO_ERROR";
  case NGHTTP2_PROTOCOL_ERROR:
    return "PROTOCOL_ERROR";
  case NGHTTP2_INTERNAL_ERROR:
    return "INTERNAL_ERROR";
  case NGHTTP2_FLOW_CONTROL_ERROR:
    return "FLOW_CONTROL_ERROR";
  case NGHTTP2_SETTINGS_TIMEOUT:
    return "SETTINGS_TIMEOUT";
  case NGHTTP2_STREAM_CLOSED:
    return "STREAM_CLOSED";
  case NGHTTP2_FRAME_SIZE_ERROR:
    return "FRAME_SIZE_ERROR";
  case NGHTTP2_REFUSED_STREAM:
    return "REFUSED_STREAM";
  case NGHTTP2_CANCEL:
    return "CANCEL";
  case NGHTTP2_COMPRESSION_ERROR:
    return "COMPRESSION_ERROR";
  case NGHTTP2_CONNECT_ERROR:
    return "CONNECT_ERROR";
  case NGHTTP2_ENHANCE_YOUR_CALM:
    return "ENHANCE_YOUR_CALM";
  case NGHTTP2_INADEQUATE_SECURITY:
    return "INADEQUATE_SECURITY";
  case NGHTTP2_HTTP_1_1_REQUIRED:
    return "HTTP_1_1_REQUIRED";
  default:
    return "unknown";
  }
}
