code: func (c *Client) FindInformation(starth, endh uint16) (fmt int, data []byte, err error) {
  	if starth == 0 || starth > endh {
  		return 0x00, nil, ErrInvalidArgument
  	}
  
  	// Acquire and reuse the txBuf, and release it after usage.
  	txBuf := <-c.chTxBuf
  	defer func() { c.chTxBuf <- txBuf }()
  
  	req := FindInformationRequest(txBuf[:5])
  	req.SetAttributeOpcode()
  	req.SetStartingHandle(starth)
  	req.SetEndingHandle(endh)
  
  	b, err := c.sendReq(req)
  	if err != nil {
  		return 0x00, nil, err
  	}
  
  	// Convert and validate the response.
  	rsp := FindInformationResponse(b)
  	switch {
  	case rsp[0] == ErrorResponseCode && len(rsp) == 5:
  		return 0x00, nil, ble.ATTError(rsp[4])
  	case rsp[0] == ErrorResponseCode && len(rsp) != 5:
  		fallthrough
  	case rsp[0] != rsp.AttributeOpcode():
  		fallthrough
  	case len(rsp) < 6:
  		fallthrough
  	case rsp.Format() == 0x01 && ((len(rsp)-2)%4) != 0:
  		fallthrough
  	case rsp.Format() == 0x02 && ((len(rsp)-2)%18) != 0:
  		return 0x00, nil, ErrInvalidResponse
  	}
  	return int(rsp.Format()), rsp.InformationData(), nil
  }
docs: // FindInformation obtains the mapping of attribute handles with their associated types.
  // This allows a Client to discover the list of attributes and their types on a server.
  // [Vol 3, Part F, 3.4.3.1 & 3.4.3.2]