bool ValidateTryGet(int index, OscArgType requestedType)
    {
        // Arg bounds.
        if (index < 0 || index >= _argInfo.Count)
        {
            StringBuilder sb = StartBuildingInvalidTryGetString(OscDebug.BuildText(this));
            sb.Append("Requested argument index "); sb.Append(index);
            sb.Append(" is out of bounds. Message has "); sb.Append(_argInfo.Count);
            sb.Append(" arguments.\n");
            Debug.LogWarning(sb.ToString());
            return(false);
        }

        // Arg type.
        OscArgInfo info = _argInfo[index];
        OscArgType type = OscConverter.ToArgType(info.tagByte);

        if (requestedType != type)
        {
            StringBuilder sb = StartBuildingInvalidTryGetString(OscDebug.BuildText(this));
            sb.Append("Argument at index "); sb.Append(index);
            sb.Append(" is not type "); sb.Append(requestedType);
            sb.Append(" ('"); sb.Append((char)OscConverter.ToTagByte(requestedType)); sb.Append("')");
            sb.Append(", it is "); sb.Append(type);
            sb.Append(" ('"); sb.Append((char)info.tagByte);
            sb.Append("').\n");
            Debug.LogWarning(sb.ToString());
            return(false);
        }

        // Data capacity.
        if (index + info.size > _argData.Count)
        {
            StringBuilder sb = StartBuildingInvalidTryGetString(OscDebug.BuildText(this));
            sb.Append("Argument at index "); sb.Append(index);
            sb.Append(" has incomplete data\n");
            Debug.LogWarning(sb.ToString());
            return(false);
        }

        return(true);
    }
    public void Add(params object[] args)
    {
        // Adaptive Set info capacity.
        int infoStartIndex = _argInfo.Count;
        int newArgCount    = infoStartIndex + args.Length;

        if (newArgCount > _argInfo.Capacity)
        {
            _argInfo.Capacity = newArgCount;
        }

        // Get info and evaluate data byte count.
        int newArgsByteCount = 0;

        foreach (object arg in args)
        {
            byte tagByte = OscConverter.ToTagByte(arg);

            int argByteCount = 0;
            switch (tagByte)
            {
            case OscConst.tagFloatByte:
            case OscConst.tagIntByte:
            case OscConst.tagCharByte:
            case OscConst.tagColorByte:
            case OscConst.tagMidiByte:
                argByteCount = 4;
                break;

            case OscConst.tagDoubleByte:
            case OscConst.tagLongByte:
            case OscConst.tagTimetagByte:
                argByteCount = 8;
                break;

            case OscConst.tagStringByte:
                argByteCount = StringOscData.EvaluateByteCount((string)arg);
                break;

            case OscConst.tagBlobByte:
                argByteCount = BlobOscData.EvaluateByteCount((byte[])arg);
                break;
            }

            _argInfo.Add(new OscArgInfo(tagByte, argByteCount));
            newArgsByteCount += argByteCount;
        }

        // AdaptiveSet data capacity.
        int totalArgsByteCount = _argData.Count + newArgsByteCount;

        if (totalArgsByteCount > _argData.Capacity)
        {
            _argData.Capacity = totalArgsByteCount;
        }

        // Store arguments directly as bytes.
        int i = infoStartIndex;

        foreach (object arg in args)
        {
            switch (_argInfo[i++].tagByte)
            {
            case OscConst.tagFloatByte: new FourByteOscData((float)arg).AddTo(_argData); break;

            case OscConst.tagIntByte: new FourByteOscData((int)arg).AddTo(_argData); break;

            case OscConst.tagCharByte: new FourByteOscData((char)arg).AddTo(_argData); break;

            case OscConst.tagColorByte: new FourByteOscData((Color32)arg).AddTo(_argData); break;

            case OscConst.tagMidiByte: new FourByteOscData((OscMidiMessage)arg).AddTo(_argData); break;

            case OscConst.tagDoubleByte: new EightByteOscData((double)arg).AddTo(_argData); break;

            case OscConst.tagLongByte: new EightByteOscData((long)arg).AddTo(_argData); break;

            case OscConst.tagTimetagByte: new EightByteOscData((OscTimeTag)arg).AddTo(_argData); break;

            case OscConst.tagStringByte: StringOscData.AddTo((string)arg, _argData); break;

            case OscConst.tagBlobByte: BlobOscData.AddTo((byte[])arg, _argData); break;

            case OscConst.tagUnsupportedByte:
                // For unsupported tags, we don't attemt to store any data. But we warn the user.
                Debug.LogWarning("Type " + arg.GetType() + " is not supported.\n");                           // TODO warnings should be optional.
                break;
            }
        }
    }