Copyright 2014 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package hpack

import (
	
)

const (
	uint32Max              = ^uint32(0)
	initialHeaderTableSize = 4096
)

type Encoder struct {
minSize is the minimum table size set by SetMaxDynamicTableSize after the previous Header Table Size Update.
maxSizeLimit is the maximum table size this encoder supports. This will protect the encoder from too large size.
tableSizeUpdate indicates whether "Header Table Size Update" is required.
NewEncoder returns a new Encoder which performs HPACK encoding. An encoded data is written to w.
WriteField encodes f into a single Write to e's underlying Writer. This function may also produce bytes for "Header Table Size Update" if necessary. If produced, it is done before encoding f.
func ( *Encoder) ( HeaderField) error {
	.buf = .buf[:0]

	if .tableSizeUpdate {
		.tableSizeUpdate = false
		if .minSize < .dynTab.maxSize {
			.buf = appendTableSize(.buf, .minSize)
		}
		.minSize = uint32Max
		.buf = appendTableSize(.buf, .dynTab.maxSize)
	}

	,  := .searchTable()
	if  {
		.buf = appendIndexed(.buf, )
	} else {
		 := .shouldIndex()
		if  {
			.dynTab.add()
		}

		if  == 0 {
			.buf = appendNewName(.buf, , )
		} else {
			.buf = appendIndexedName(.buf, , , )
		}
	}
	,  := .w.Write(.buf)
	if  == nil &&  != len(.buf) {
		 = io.ErrShortWrite
	}
	return 
}
searchTable searches f in both stable and dynamic header tables. The static header table is searched first. Only when there is no exact match for both name and value, the dynamic header table is then searched. If there is no match, i is 0. If both name and value match, i is the matched index and nameValueMatch becomes true. If only name matches, i points to that index and nameValueMatch becomes false.
func ( *Encoder) ( HeaderField) ( uint64,  bool) {
	,  = staticTable.search()
	if  {
		return , true
	}

	,  := .dynTab.table.search()
	if  || ( == 0 &&  != 0) {
		return  + uint64(staticTable.len()), 
	}

	return , false
}
SetMaxDynamicTableSize changes the dynamic header table size to v. The actual size is bounded by the value passed to SetMaxDynamicTableSizeLimit.
func ( *Encoder) ( uint32) {
	if  > .maxSizeLimit {
		 = .maxSizeLimit
	}
	if  < .minSize {
		.minSize = 
	}
	.tableSizeUpdate = true
	.dynTab.setMaxSize()
}
SetMaxDynamicTableSizeLimit changes the maximum value that can be specified in SetMaxDynamicTableSize to v. By default, it is set to 4096, which is the same size of the default dynamic header table size described in HPACK specification. If the current maximum dynamic header table size is strictly greater than v, "Header Table Size Update" will be done in the next WriteField call and the maximum dynamic header table size is truncated to v.
shouldIndex reports whether f should be indexed.
func ( *Encoder) ( HeaderField) bool {
	return !.Sensitive && .Size() <= .dynTab.maxSize
}
appendIndexed appends index i, as encoded in "Indexed Header Field" representation, to dst and returns the extended buffer.
func ( []byte,  uint64) []byte {
	 := len()
	 = appendVarInt(, 7, )
	[] |= 0x80
	return 
}
appendNewName appends f, as encoded in one of "Literal Header field - New Name" representation variants, to dst and returns the extended buffer. If f.Sensitive is true, "Never Indexed" representation is used. If f.Sensitive is false and indexing is true, "Incremental Indexing" representation is used.
func ( []byte,  HeaderField,  bool) []byte {
	 = append(, encodeTypeByte(, .Sensitive))
	 = appendHpackString(, .Name)
	return appendHpackString(, .Value)
}
appendIndexedName appends f and index i referring indexed name entry, as encoded in one of "Literal Header field - Indexed Name" representation variants, to dst and returns the extended buffer. If f.Sensitive is true, "Never Indexed" representation is used. If f.Sensitive is false and indexing is true, "Incremental Indexing" representation is used.
func ( []byte,  HeaderField,  uint64,  bool) []byte {
	 := len()
	var  byte
	if  {
		 = 6
	} else {
		 = 4
	}
	 = appendVarInt(, , )
	[] |= encodeTypeByte(, .Sensitive)
	return appendHpackString(, .Value)
}
appendTableSize appends v, as encoded in "Header Table Size Update" representation, to dst and returns the extended buffer.
func ( []byte,  uint32) []byte {
	 := len()
	 = appendVarInt(, 5, uint64())
	[] |= 0x20
	return 
}
appendVarInt appends i, as encoded in variable integer form using n bit prefix, to dst and returns the extended buffer. See http://http2.github.io/http2-spec/compression.html#integer.representation
func ( []byte,  byte,  uint64) []byte {
	 := uint64((1 << ) - 1)
	if  <  {
		return append(, byte())
	}
	 = append(, byte())
	 -= 
	for ;  >= 128;  >>= 7 {
		 = append(, byte(0x80|(&0x7f)))
	}
	return append(, byte())
}
appendHpackString appends s, as encoded in "String Literal" representation, to dst and returns the extended buffer. s will be encoded in Huffman codes only when it produces strictly shorter byte string.
func ( []byte,  string) []byte {
	 := HuffmanEncodeLength()
	if  < uint64(len()) {
		 := len()
		 = appendVarInt(, 7, )
		 = AppendHuffmanString(, )
		[] |= 0x80
	} else {
		 = appendVarInt(, 7, uint64(len()))
		 = append(, ...)
	}
	return 
}
encodeTypeByte returns type byte. If sensitive is true, type byte for "Never Indexed" representation is returned. If sensitive is false and indexing is true, type byte for "Incremental Indexing" representation is returned. Otherwise, type byte for "Without Indexing" is returned.
func (,  bool) byte {
	if  {
		return 0x10
	}
	if  {
		return 0x40
	}
	return 0