ipp_length(ipp_t *ipp,			/* I - IPP message or collection */
           int   collection)		/* I - 1 if a collection, 0 otherwise */
{
  int			i;		/* Looping var */
  size_t		bytes;		/* Number of bytes */
  ipp_attribute_t	*attr;		/* Current attribute */
  ipp_tag_t		group;		/* Current group */
  _ipp_value_t		*value;		/* Current value */


  DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection));

  if (!ipp)
  {
    DEBUG_puts("4ipp_length: Returning 0 bytes");
    return (0);
  }

 /*
  * Start with 8 bytes for the IPP message header...
  */

  bytes = collection ? 0 : 8;

 /*
  * Then add the lengths of each attribute...
  */

  group = IPP_TAG_ZERO;

  for (attr = ipp->attrs; attr != NULL; attr = attr->next)
  {
    if (attr->group_tag != group && !collection)
    {
      group = attr->group_tag;
      if (group == IPP_TAG_ZERO)
	continue;

      bytes ++;	/* Group tag */
    }

    if (!attr->name)
      continue;

    DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
                  "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));

    if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION)
      bytes += (size_t)attr->num_values;/* Value tag for each value */
    else
      bytes += (size_t)(5 * attr->num_values);
					/* Value tag for each value */
    bytes += (size_t)(2 * attr->num_values);
					/* Name lengths */
    bytes += strlen(attr->name);	/* Name */
    bytes += (size_t)(2 * attr->num_values);
					/* Value lengths */

    if (collection)
      bytes += 5;			/* Add membername overhead */

    switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
    {
      case IPP_TAG_UNSUPPORTED_VALUE :
      case IPP_TAG_DEFAULT :
      case IPP_TAG_UNKNOWN :
      case IPP_TAG_NOVALUE :
      case IPP_TAG_NOTSETTABLE :
      case IPP_TAG_DELETEATTR :
      case IPP_TAG_ADMINDEFINE :
          break;

      case IPP_TAG_INTEGER :
      case IPP_TAG_ENUM :
          bytes += (size_t)(4 * attr->num_values);
	  break;

      case IPP_TAG_BOOLEAN :
          bytes += (size_t)attr->num_values;
	  break;

      case IPP_TAG_TEXT :
      case IPP_TAG_NAME :
      case IPP_TAG_KEYWORD :
      case IPP_TAG_URI :
      case IPP_TAG_URISCHEME :
      case IPP_TAG_CHARSET :
      case IPP_TAG_LANGUAGE :
      case IPP_TAG_MIMETYPE :
	  for (i = 0, value = attr->values;
	       i < attr->num_values;
	       i ++, value ++)
	    if (value->string.text)
	      bytes += strlen(value->string.text);
	  break;

      case IPP_TAG_DATE :
          bytes += (size_t)(11 * attr->num_values);
	  break;

      case IPP_TAG_RESOLUTION :
          bytes += (size_t)(9 * attr->num_values);
	  break;

      case IPP_TAG_RANGE :
          bytes += (size_t)(8 * attr->num_values);
	  break;

      case IPP_TAG_TEXTLANG :
      case IPP_TAG_NAMELANG :
          bytes += (size_t)(4 * attr->num_values);
					/* Charset + text length */

	  for (i = 0, value = attr->values;
	       i < attr->num_values;
	       i ++, value ++)
	  {
	    if (value->string.language)
	      bytes += strlen(value->string.language);

	    if (value->string.text)
	      bytes += strlen(value->string.text);
	  }
	  break;

      case IPP_TAG_BEGIN_COLLECTION :
	  for (i = 0, value = attr->values;
	       i < attr->num_values;
	       i ++, value ++)
            bytes += ipp_length(value->collection, 1);
	  break;

      default :
	  for (i = 0, value = attr->values;
	       i < attr->num_values;
	       i ++, value ++)
            bytes += (size_t)value->unknown.length;
	  break;
    }
  }

 /*
  * Finally, add 1 byte for the "end of attributes" tag or 5 bytes
  * for the "end of collection" tag and return...
  */

  if (collection)
    bytes += 5;
  else
    bytes ++;

  DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));

  return (bytes);
}