func (s *Server) handleFindByTypeValueRequest(r FindByTypeValueRequest) []byte {
	switch {
	case len(r) < 7:
		return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
	case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle():
		return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
	}
	rsp := FindByTypeValueResponse(s.txBuf)
	rsp.SetAttributeOpcode()
	buf := bytes.NewBuffer(rsp.HandleInformationList())
	buf.Reset()
	for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) {
		v, starth, endh := a.v, a.h, a.endh
		if !(ble.UUID(a.typ).Equal(ble.UUID16(r.AttributeType()))) {
			continue
		}
		if v == nil {
			buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-7+1))
			e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2))
			if e != ble.ErrSuccess || buf2.Len() > len(s.txBuf)-7 {
				return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
			}
			endh = a.h
		}
		if !(ble.UUID(v).Equal(ble.UUID(r.AttributeValue()))) {
			continue
		}
		if buf.Len()+4 > buf.Cap() {
			break
		}
		binary.Write(buf, binary.LittleEndian, starth)
		binary.Write(buf, binary.LittleEndian, endh)
	}
	if buf.Len() == 0 {
		return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound)
	}
	return rsp[:1+buf.Len()]
}
handle Find By Type Value request. [Vol 3, Part F, 3.4.3.3 & 3.4.3.4]
package att
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"time"
	"github.com/currantlabs/ble"
)
type conn struct {
	ble.Conn
	svr  *Server
	cccs map[uint16]uint16
	nn   map[uint16]ble.Notifier
	in   map[uint16]ble.Notifier
}
type Server struct {
	conn *conn
	db   *DB
	rxMTU     int
	txBuf     []byte
	chNotBuf  chan []byte
	chIndBuf  chan []byte
	chConfirm chan bool
	dummyRspWriter ble.ResponseWriter
}
func NewServer(db *DB, l2c ble.Conn) (*Server, error) {
	mtu := l2c.RxMTU()
	if mtu < ble.DefaultMTU || mtu > ble.MaxMTU {
		return nil, fmt.Errorf("invalid MTU")
	}
	s := &Server{
		conn: &conn{
			Conn: l2c,
			cccs: make(map[uint16]uint16),
			in:   make(map[uint16]ble.Notifier),
			nn:   make(map[uint16]ble.Notifier),
		},
		db: db,
		rxMTU:     mtu,
		txBuf:     make([]byte, ble.DefaultMTU, ble.DefaultMTU),
		chNotBuf:  make(chan []byte, 1),
		chIndBuf:  make(chan []byte, 1),
		chConfirm: make(chan bool),
		dummyRspWriter: ble.NewResponseWriter(nil),
	}
	s.conn.svr = s
	s.chNotBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU)
	s.chIndBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU)
	return s, nil
}
func (s *Server) notify(h uint16, data []byte) (int, error) {
	nBuf := <-s.chNotBuf
	defer func() { s.chNotBuf <- nBuf }()
	rsp := HandleValueNotification(nBuf)
	rsp.SetAttributeOpcode()
	rsp.SetAttributeHandle(h)
	buf := bytes.NewBuffer(rsp.AttributeValue())
	buf.Reset()
	if len(data) > buf.Cap() {
		data = data[:buf.Cap()]
	}
	buf.Write(data)
	return s.conn.Write(rsp[:3+buf.Len()])
}
func (s *Server) indicate(h uint16, data []byte) (int, error) {
	iBuf := <-s.chIndBuf
	defer func() { s.chIndBuf <- iBuf }()
	rsp := HandleValueIndication(iBuf)
	rsp.SetAttributeOpcode()
	rsp.SetAttributeHandle(h)
	buf := bytes.NewBuffer(rsp.AttributeValue())
	buf.Reset()
	if len(data) > buf.Cap() {
		data = data[:buf.Cap()]
	}
	buf.Write(data)
	n, err := s.conn.Write(rsp[:3+buf.Len()])
	if err != nil {
		return n, err
	}
	select {
	case _, ok := <-s.chConfirm:
		if !ok {
			return 0, io.ErrClosedPipe
		}
		return n, nil
	case <-time.After(time.Second * 30):
		return 0, ErrSeqProtoTimeout
	}
}
func (s *Server) Loop() {
	type sbuf struct {
		buf []byte
		len int
	}
	pool := make(chan *sbuf, 2)
	pool <- &sbuf{buf: make([]byte, s.rxMTU)}
	pool <- &sbuf{buf: make([]byte, s.rxMTU)}
	seq := make(chan *sbuf)
	go func() {
		b := <-pool
		for {
			n, err := s.conn.Read(b.buf)
			if n == 0 || err != nil {
				close(seq)
				close(s.chConfirm)
				_ = s.conn.Close()
				return
			}
			if b.buf[0] == HandleValueConfirmationCode {
				select {
				case s.chConfirm <- true:
				default:
					logger.Error("server", "recieved a spurious confirmation", nil)
				}
				continue
			}
			b.len = n
			seq <- b    
			b = <-pool  
		}
	}()
	for req := range seq {
		if rsp := s.handleRequest(req.buf[:req.len]); rsp != nil {
			if len(rsp) != 0 {
				s.conn.Write(rsp)
			}
		}
		pool <- req
	}
	for h, ccc := range s.conn.cccs {
		if ccc != 0 {
			logger.Info("cleanup", "ccc", fmt.Sprintf("0x%02X", ccc))
		}
		if ccc&cccIndicate != 0 {
			s.conn.in[h].Close()
		}
		if ccc&cccNotify != 0 {
			s.conn.nn[h].Close()
		}
	}
}
func (s *Server) handleRequest(b []byte) []byte {
	var resp []byte
	logger.Debug("server", "req", fmt.Sprintf("% X", b))
	switch reqType := b[0]; reqType {
	case ExchangeMTURequestCode:
		resp = s.handleExchangeMTURequest(b)
	case FindInformationRequestCode:
		resp = s.handleFindInformationRequest(b)
	case FindByTypeValueRequestCode:
		resp = s.handleFindByTypeValueRequest(b)
	case ReadByTypeRequestCode:
		resp = s.handleReadByTypeRequest(b)
	case ReadRequestCode:
		resp = s.handleReadRequest(b)
	case ReadBlobRequestCode:
		resp = s.handleReadBlobRequest(b)
	case ReadByGroupTypeRequestCode:
		resp = s.handleReadByGroupRequest(b)
	case WriteRequestCode:
		resp = s.handleWriteRequest(b)
	case WriteCommandCode:
		s.handleWriteCommand(b)
	case ReadMultipleRequestCode,
		PrepareWriteRequestCode,
		ExecuteWriteRequestCode,
		SignedWriteCommandCode:
		fallthrough
	default:
		resp = newErrorResponse(reqType, 0x0000, ble.ErrReqNotSupp)
	}
	logger.Debug("server", "rsp", fmt.Sprintf("% X", resp))
	return resp
}
func (s *Server) handleExchangeMTURequest(r ExchangeMTURequest) []byte {
	switch {
	case len(r) != 3:
		fallthrough
	case r.ClientRxMTU() < 23:
		return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
	}
	txMTU := int(r.ClientRxMTU())
	s.conn.SetTxMTU(txMTU)
	if txMTU != len(s.txBuf) {
		defer func() {
			s.txBuf = make([]byte, txMTU, txMTU)
			<-s.chNotBuf
			s.chNotBuf <- make([]byte, txMTU, txMTU)
			<-s.chIndBuf
			s.chIndBuf <- make([]byte, txMTU, txMTU)
		}()
	}
	rsp := ExchangeMTUResponse(s.txBuf)
	rsp.SetAttributeOpcode()
	rsp.SetServerRxMTU(uint16(s.rxMTU))
	return rsp[:3]
}
func (s *Server) handleFindInformationRequest(r FindInformationRequest) []byte {
	switch {
	case len(r) != 5:
		return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
	case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle():
		return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
	}
	rsp := FindInformationResponse(s.txBuf)
	rsp.SetAttributeOpcode()
	rsp.SetFormat(0x00)
	buf := bytes.NewBuffer(rsp.InformationData())
	buf.Reset()
	for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) {
		if rsp.Format() == 0 {
			rsp.SetFormat(0x01)
			if a.typ.Len() == 16 {
				rsp.SetFormat(0x02)
			}
		}
		if rsp.Format() == 0x01 && a.typ.Len() != 2 {
			break
		}
		if rsp.Format() == 0x02 && a.typ.Len() != 16 {
			break
		}
		if buf.Len()+2+a.typ.Len() > buf.Cap() {
			break
		}
		binary.Write(buf, binary.LittleEndian, a.h)
		binary.Write(buf, binary.LittleEndian, a.typ)
	}
	if rsp.Format() == 0 {
		return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound)
	}
	return rsp[:2+buf.Len()]
}