/// <summary>
    /// Writes a string with a given encoding to a byte blob at specified index, expanding message capacity if necessary.
    /// </summary>
    public OscMessage SetBlob(int index, Encoding encoding, string value)
    {
        OscArgInfo info           = new OscArgInfo(OscConst.tagBlobByte, BlobOscData.EvaluateByteCount(value, encoding));
        int        dataStartIndex = AdaptiveSet(index, info);

        if (!BlobOscData.TryWriteTo(value, encoding, _argData, ref dataStartIndex))
        {
            Debug.Log(OscDebug.FailedWritingBytesWarning(this));
        }
        return(this);
    }
    /// <summary>
    /// Writes a list of values to a byte blob at specified index, expanding message capacity if necessary.
    /// </summary>
    public OscMessage SetBlob(int index, IList <int> values)
    {
        OscArgInfo info           = new OscArgInfo(OscConst.tagBlobByte, (1 + values.Count) * FourByteOscData.byteCount);
        int        dataStartIndex = AdaptiveSet(index, info);

        if (!BlobOscData.TryWriteTo(values, _argData, ref dataStartIndex))
        {
            Debug.Log(OscDebug.FailedWritingBytesWarning(this));
        }
        return(this);
    }
    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);
    }
    int AdaptiveSet(int index, OscArgInfo info)
    {
        // Check for change.
        bool       shouldAddArg = index >= _argInfo.Count;
        OscArgInfo oldInfo      = shouldAddArg ? OscArgInfo.undefinedInfo : _argInfo[index];

        if (info.tagByte == oldInfo.tagByte && info.size == oldInfo.size)
        {
            // No adaptation needed.
            return(GetDataIndex(index));
        }

        // Adapt info list.
        if (shouldAddArg)
        {
            int requiredArgCount = index + 1;
            if (requiredArgCount < _argInfo.Capacity)
            {
                _argInfo.Capacity = requiredArgCount;
            }
            for (int i = _argInfo.Count; i < requiredArgCount; i++)
            {
                _argInfo.Add(OscArgInfo.nullInfo);
            }
        }

        // Get start index and old byte count for data.
        int oldDataSize    = 0;
        int dataStartIndex = 0;

        for (int i = 0; i < _argInfo.Count; i++)
        {
            if (i == index)
            {
                dataStartIndex = oldDataSize;
            }
            oldDataSize += _argInfo[i].size;
        }

        // Adapt data list.
        int byteDelta = info.size - oldInfo.size;

        if (byteDelta > 0)
        {
            int newDataByteCount = oldDataSize + byteDelta;
            if (newDataByteCount > _argData.Capacity)
            {
                _argData.Capacity = newDataByteCount;
            }
            // If last argument then add to end, otherwise insert to preserve existing data.
            if (index == _argInfo.Count - 1)
            {
                for (int i = 0; i < byteDelta; i++)
                {
                    _argData.Add(0);
                }
            }
            else
            {
                for (int i = 0; i < byteDelta; i++)
                {
                    _argData.Insert(dataStartIndex, 0);
                }
            }
        }
        else if (byteDelta < 0)
        {
            _argData.RemoveRange(dataStartIndex, -byteDelta);
        }

        // Overwrite info.
        _argInfo[index] = info;

        // Request byte count update.
        _dirtySize = true;

        return(dataStartIndex);
    }