// 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.

// Flow control

package http2

// inflowMinRefresh is the minimum number of bytes we'll send for a
// flow control window update.
const inflowMinRefresh = 4 << 10

// maxFlowWindow is the maximum size of a flow control window.
const maxFlowWindow = (1 << 31) - 1

// inflow accounts for an inbound flow control window.
// It tracks both the latest window sent to the peer (used for enforcement)
// and the accumulated unsent window.
type inflow struct {
	avail  int32
	unsent int32
}

// init sets the initial window.
func ( *inflow) ( int32) {
	.avail = 
}

// add adds n bytes to the window, with a maximum window size of max,
// indicating that the peer can now send us more data.
// For example, the user read from a {Request,Response} body and consumed
// some of the buffered data, so the peer can now send more.
// It returns the number of bytes to send in a WINDOW_UPDATE frame to the peer.
// Window updates are accumulated and sent when the unsent capacity
// is at least inflowMinRefresh or will at least double the peer's available window.
func ( *inflow) ( int) ( int32) {
	if  < 0 {
		panic("negative update")
	}
	 := int64(.unsent) + int64()
	// "A sender MUST NOT allow a flow-control window to exceed 2^31-1 octets."
	// RFC 7540 Section 6.9.1.
	const  = 1<<31 - 1
	if +int64(.avail) >  {
		panic("flow control update exceeds maximum window size")
	}
	.unsent = int32()
	if .unsent < inflowMinRefresh && .unsent < .avail {
		// If there aren't at least inflowMinRefresh bytes of window to send,
		// and this update won't at least double the window, buffer the update for later.
		return 0
	}
	.avail += .unsent
	.unsent = 0
	return int32()
}

// take attempts to take n bytes from the peer's flow control window.
// It reports whether the window has available capacity.
func ( *inflow) ( uint32) bool {
	if  > uint32(.avail) {
		return false
	}
	.avail -= int32()
	return true
}

// takeInflows attempts to take n bytes from two inflows,
// typically connection-level and stream-level flows.
// It reports whether both windows have available capacity.
func (,  *inflow,  uint32) bool {
	if  > uint32(.avail) ||  > uint32(.avail) {
		return false
	}
	.avail -= int32()
	.avail -= int32()
	return true
}

// connOutflow is connection-level outbound flow control.
type connOutflow struct {
	initial int32 // SETTINGS_INITIAL_WINDOW_SIZE, changes with settings updates
	n       int32 // connection-level flow control window
	flowErr bool  // set when a flow control error is encountered
}

func ( *connOutflow) () {
	.initial = initialWindowSize // initial stream window size
	.n = initialWindowSize       // current connection window size
}

func ( *connOutflow) ( int64) bool {
	if  > maxFlowWindow {
		.flowErr = true
		return false
	}
	.initial = int32()
	return true
}

func ( *connOutflow) ( int32) bool {
	 := int64(.n) + int64()
	if  > maxFlowWindow {
		.flowErr = true
		return false
	}
	.n += 
	return true
}

// outflow is the stream-level outbound flow control window's size.
type outflow struct {
	_ incomparable

	// delta is the difference between the stream's flow control window and
	// the connection's initial window size (conn.initial).
	//
	// Another view is that delta is the number of flow control bytes provided to this
	// stream in WINDOW_UPDATE frames, less the number of bytes sent on the stream.
	delta int32

	// conn points to the shared connection-level outflow that is
	// shared by all streams on that conn.
	conn *connOutflow
}

func ( *outflow) () (int32, bool) {
	if .conn == nil {
		return maxFlowWindow, true // only happens in tests
	}
	if .conn.flowErr {
		// Block all sending once any stream observes a flow control error.
		return 0, false
	}
	 := int64(.conn.initial) + int64(.delta)
	if  > maxFlowWindow {
		.conn.flowErr = true
		return 0, false
	}
	return min(int32(), .conn.n), true
}

func ( *outflow) ( int32) {
	if .conn == nil {
		return // only happens in tests
	}
	,  := .available()
	if  >  {
		panic("internal error: took too much")
	}
	.delta -= 
	.conn.n -= 
}

// add adds n bytes (positive or negative) to the flow control window.
// It returns false if the sum would exceed 2^31-1.
func ( *outflow) ( int32) bool {
	if .conn == nil {
		return true // only happens in tests
	}
	 := int64(.conn.initial) + int64(.delta)
	if  > maxFlowWindow {
		// An earlier change to the initial window pushed this stream over the limit.
		// This is a connection-level flow control error.
		.conn.flowErr = true
		return false
	}
	if +int64() > maxFlowWindow {
		// This update would push the stream over the limit.
		// This is a stream-level flow control error.
		return false
	}
	.delta += 
	return true
}