//go:build !js

package websocket

import (
	
	
	
	
	
	

	
)

// opcode represents a WebSocket opcode.
type opcode int

// https://tools.ietf.org/html/rfc6455#section-11.8.
const (
	opContinuation opcode = iota
	opText
	opBinary
	// 3 - 7 are reserved for further non-control frames.
	_
	_
	_
	_
	_
	opClose
	opPing
	opPong
	// 11-16 are reserved for further control frames.
)

// header represents a WebSocket frame header.
// See https://tools.ietf.org/html/rfc6455#section-5.2.
type header struct {
	fin    bool
	rsv1   bool
	rsv2   bool
	rsv3   bool
	opcode opcode

	payloadLength int64

	masked  bool
	maskKey uint32
}

// readFrameHeader reads a header from the reader.
// See https://tools.ietf.org/html/rfc6455#section-5.2.
func ( *bufio.Reader,  []byte) ( header,  error) {
	defer errd.Wrap(&, "failed to read frame header")

	,  := .ReadByte()
	if  != nil {
		return header{}, 
	}

	.fin = &(1<<7) != 0
	.rsv1 = &(1<<6) != 0
	.rsv2 = &(1<<5) != 0
	.rsv3 = &(1<<4) != 0

	.opcode = opcode( & 0xf)

	,  = .ReadByte()
	if  != nil {
		return header{}, 
	}

	.masked = &(1<<7) != 0

	 :=  &^ (1 << 7)
	switch {
	case  < 126:
		.payloadLength = int64()
	case  == 126:
		_,  = io.ReadFull(, [:2])
		.payloadLength = int64(binary.BigEndian.Uint16())
	case  == 127:
		_,  = io.ReadFull(, )
		.payloadLength = int64(binary.BigEndian.Uint64())
	}
	if  != nil {
		return header{}, 
	}

	if .payloadLength < 0 {
		return header{}, fmt.Errorf("received negative payload length: %v", .payloadLength)
	}

	if .masked {
		_,  = io.ReadFull(, [:4])
		if  != nil {
			return header{}, 
		}
		.maskKey = binary.LittleEndian.Uint32()
	}

	return , nil
}

// maxControlPayload is the maximum length of a control frame payload.
// See https://tools.ietf.org/html/rfc6455#section-5.5.
const maxControlPayload = 125

// writeFrameHeader writes the bytes of the header to w.
// See https://tools.ietf.org/html/rfc6455#section-5.2
func ( header,  *bufio.Writer,  []byte) ( error) {
	defer errd.Wrap(&, "failed to write frame header")

	var  byte
	if .fin {
		 |= 1 << 7
	}
	if .rsv1 {
		 |= 1 << 6
	}
	if .rsv2 {
		 |= 1 << 5
	}
	if .rsv3 {
		 |= 1 << 4
	}

	 |= byte(.opcode)

	 = .WriteByte()
	if  != nil {
		return 
	}

	 := byte(0)
	if .masked {
		 |= 1 << 7
	}

	switch {
	case .payloadLength > math.MaxUint16:
		 |= 127
	case .payloadLength > 125:
		 |= 126
	case .payloadLength >= 0:
		 |= byte(.payloadLength)
	}
	 = .WriteByte()
	if  != nil {
		return 
	}

	switch {
	case .payloadLength > math.MaxUint16:
		binary.BigEndian.PutUint64(, uint64(.payloadLength))
		_,  = .Write()
	case .payloadLength > 125:
		binary.BigEndian.PutUint16(, uint16(.payloadLength))
		_,  = .Write([:2])
	}
	if  != nil {
		return 
	}

	if .masked {
		binary.LittleEndian.PutUint32(, .maskKey)
		_,  = .Write([:4])
		if  != nil {
			return 
		}
	}

	return nil
}

// mask applies the WebSocket masking algorithm to p
// with the given key.
// See https://tools.ietf.org/html/rfc6455#section-5.3
//
// The returned value is the correctly rotated key to
// to continue to mask/unmask the message.
//
// It is optimized for LittleEndian and expects the key
// to be in little endian.
//
// See https://github.com/golang/go/issues/31586
func ( uint32,  []byte) uint32 {
	if len() >= 8 {
		 := uint64()<<32 | uint64()

		// At some point in the future we can clean these unrolled loops up.
		// See https://github.com/golang/go/issues/31586#issuecomment-487436401

		// Then we xor until b is less than 128 bytes.
		for len() >= 128 {
			 := binary.LittleEndian.Uint64()
			binary.LittleEndian.PutUint64(, ^)
			 = binary.LittleEndian.Uint64([8:16])
			binary.LittleEndian.PutUint64([8:16], ^)
			 = binary.LittleEndian.Uint64([16:24])
			binary.LittleEndian.PutUint64([16:24], ^)
			 = binary.LittleEndian.Uint64([24:32])
			binary.LittleEndian.PutUint64([24:32], ^)
			 = binary.LittleEndian.Uint64([32:40])
			binary.LittleEndian.PutUint64([32:40], ^)
			 = binary.LittleEndian.Uint64([40:48])
			binary.LittleEndian.PutUint64([40:48], ^)
			 = binary.LittleEndian.Uint64([48:56])
			binary.LittleEndian.PutUint64([48:56], ^)
			 = binary.LittleEndian.Uint64([56:64])
			binary.LittleEndian.PutUint64([56:64], ^)
			 = binary.LittleEndian.Uint64([64:72])
			binary.LittleEndian.PutUint64([64:72], ^)
			 = binary.LittleEndian.Uint64([72:80])
			binary.LittleEndian.PutUint64([72:80], ^)
			 = binary.LittleEndian.Uint64([80:88])
			binary.LittleEndian.PutUint64([80:88], ^)
			 = binary.LittleEndian.Uint64([88:96])
			binary.LittleEndian.PutUint64([88:96], ^)
			 = binary.LittleEndian.Uint64([96:104])
			binary.LittleEndian.PutUint64([96:104], ^)
			 = binary.LittleEndian.Uint64([104:112])
			binary.LittleEndian.PutUint64([104:112], ^)
			 = binary.LittleEndian.Uint64([112:120])
			binary.LittleEndian.PutUint64([112:120], ^)
			 = binary.LittleEndian.Uint64([120:128])
			binary.LittleEndian.PutUint64([120:128], ^)
			 = [128:]
		}

		// Then we xor until b is less than 64 bytes.
		for len() >= 64 {
			 := binary.LittleEndian.Uint64()
			binary.LittleEndian.PutUint64(, ^)
			 = binary.LittleEndian.Uint64([8:16])
			binary.LittleEndian.PutUint64([8:16], ^)
			 = binary.LittleEndian.Uint64([16:24])
			binary.LittleEndian.PutUint64([16:24], ^)
			 = binary.LittleEndian.Uint64([24:32])
			binary.LittleEndian.PutUint64([24:32], ^)
			 = binary.LittleEndian.Uint64([32:40])
			binary.LittleEndian.PutUint64([32:40], ^)
			 = binary.LittleEndian.Uint64([40:48])
			binary.LittleEndian.PutUint64([40:48], ^)
			 = binary.LittleEndian.Uint64([48:56])
			binary.LittleEndian.PutUint64([48:56], ^)
			 = binary.LittleEndian.Uint64([56:64])
			binary.LittleEndian.PutUint64([56:64], ^)
			 = [64:]
		}

		// Then we xor until b is less than 32 bytes.
		for len() >= 32 {
			 := binary.LittleEndian.Uint64()
			binary.LittleEndian.PutUint64(, ^)
			 = binary.LittleEndian.Uint64([8:16])
			binary.LittleEndian.PutUint64([8:16], ^)
			 = binary.LittleEndian.Uint64([16:24])
			binary.LittleEndian.PutUint64([16:24], ^)
			 = binary.LittleEndian.Uint64([24:32])
			binary.LittleEndian.PutUint64([24:32], ^)
			 = [32:]
		}

		// Then we xor until b is less than 16 bytes.
		for len() >= 16 {
			 := binary.LittleEndian.Uint64()
			binary.LittleEndian.PutUint64(, ^)
			 = binary.LittleEndian.Uint64([8:16])
			binary.LittleEndian.PutUint64([8:16], ^)
			 = [16:]
		}

		// Then we xor until b is less than 8 bytes.
		for len() >= 8 {
			 := binary.LittleEndian.Uint64()
			binary.LittleEndian.PutUint64(, ^)
			 = [8:]
		}
	}

	// Then we xor until b is less than 4 bytes.
	for len() >= 4 {
		 := binary.LittleEndian.Uint32()
		binary.LittleEndian.PutUint32(, ^)
		 = [4:]
	}

	// xor remaining bytes.
	for  := range  {
		[] ^= byte()
		 = bits.RotateLeft32(, -8)
	}

	return 
}