/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public override void Deflate(Stream destination) { // Allocate temporary cache MemoryStream cache = new MemoryStream(); // Write header type TDSUtilities.WriteUShort(cache, (ushort)TDSHeaderType.QueryNotifications); // Write notification ID string TDSUtilities.WriteUShort(cache, (ushort)(string.IsNullOrEmpty(NotifyID) ? 0 : NotifyID.Length)); TDSUtilities.WriteString(cache, NotifyID); // Write service broker deployment TDSUtilities.WriteUShort(cache, (ushort)(string.IsNullOrEmpty(SerciceBrokerDeployment) ? 0 : SerciceBrokerDeployment.Length)); TDSUtilities.WriteString(cache, SerciceBrokerDeployment); // Write timeout TDSUtilities.WriteUInt(cache, Timeout); // Write the header length including self into the destination TDSUtilities.WriteUInt(destination, (uint)(cache.Length + 4)); // Write cached header data cache.WriteTo(destination); }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public override void Deflate(Stream destination) { // Write token identifier destination.WriteByte((byte)TDSTokenType.Error); // Calculate the length ushort totalLength = (ushort)(sizeof(uint) // Number + sizeof(byte) // State + sizeof(byte) // Class + sizeof(ushort) + (string.IsNullOrEmpty(Message) ? 0 : Message.Length) * sizeof(char) // Message + sizeof(byte) + (string.IsNullOrEmpty(ServerName) ? 0 : ServerName.Length) * sizeof(char) // Server Name + sizeof(byte) + (string.IsNullOrEmpty(ProcedureName) ? 0 : ProcedureName.Length) * sizeof(char) // Procedure Name + sizeof(uint)); // Line number // Write token length TDSUtilities.WriteUShort(destination, totalLength); // Write the number TDSUtilities.WriteUInt(destination, Number); // Write state destination.WriteByte((byte)State); // Write class destination.WriteByte((byte)Class); // Write message text length TDSUtilities.WriteUShort(destination, (ushort)(string.IsNullOrEmpty(Message) ? 0 : Message.Length)); // Write the message itself TDSUtilities.WriteString(destination, Message); // Write server name length destination.WriteByte((byte)(string.IsNullOrEmpty(ServerName) ? 0 : ServerName.Length)); // Write server name TDSUtilities.WriteString(destination, ServerName); // Write procedure name length destination.WriteByte((byte)(string.IsNullOrEmpty(ProcedureName) ? 0 : ProcedureName.Length)); // Write procedure name TDSUtilities.WriteString(destination, ProcedureName); // Write the line number TDSUtilities.WriteUInt(destination, Line); }
/// <summary> /// Deflate state into the stream /// </summary> public virtual void Deflate(Stream destination) { // Allocate temporary memory stream MemoryStream cache = new MemoryStream(); // Write database cache.WriteByte((byte)(string.IsNullOrEmpty(Database) ? 0 : Database.Length)); TDSUtilities.WriteString(cache, Database); // Write collation if (Collation == null) { // We don't have a collation cache.WriteByte(0); } else { // Write collation length cache.WriteByte((byte)Collation.Length); // Write it cache.Write(Collation, 0, Collation.Length); } // Write language cache.WriteByte((byte)(string.IsNullOrEmpty(Language) ? 0 : Language.Length)); TDSUtilities.WriteString(cache, Language); // Check if we have options if (Options != null) { // Iterate through all options and deflate them foreach (TDSSessionStateOption option in Options) { // Deflate into the cache to calcualte the overall length option.Deflate(cache); } } // Write the length TDSUtilities.WriteUInt(destination, (uint)cache.Length); // Write the data itself cache.WriteTo(destination); }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public virtual void Deflate(Stream destination) { // Write column number destination.WriteByte(Number); // Write table number destination.WriteByte(TableNumber); // Write status number destination.WriteByte((byte)Status); // Check if we have a name if (Name != null) { // Write length destination.WriteByte((byte)Name.Length); // Write data TDSUtilities.WriteString(destination, Name); } }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public virtual void Deflate(Stream destination) { // Write protocol value destination.WriteByte((byte)Protocol); // Based on the protocol type read the rest of the token switch (Protocol) { default: case TDSRoutingEnvChangeTokenValueType.TCP: { // Write port TDSUtilities.WriteUShort(destination, (ushort)ProtocolProperty); // Write alternate server name length TDSUtilities.WriteUShort(destination, (ushort)(string.IsNullOrEmpty(AlternateServer) ? 0 : AlternateServer.Length)); // Write alternate server name TDSUtilities.WriteString(destination, AlternateServer); break; } } }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public override void Deflate(Stream destination) { // Calculate total length by adding strings uint totalPacketLength = (uint)(FixedPacketLength + (uint)(string.IsNullOrEmpty(HostName) ? 0 : HostName.Length * 2) // HostName + (uint)(string.IsNullOrEmpty(UserID) ? 0 : UserID.Length * 2) // UserID + (uint)(string.IsNullOrEmpty(Password) ? 0 : Password.Length * 2) // Password + (uint)(string.IsNullOrEmpty(ApplicationName) ? 0 : ApplicationName.Length * 2) // ApplicationName + (uint)(string.IsNullOrEmpty(ServerName) ? 0 : ServerName.Length * 2) // ServerName + (uint)(string.IsNullOrEmpty(LibraryName) ? 0 : LibraryName.Length * 2) // LibraryName + (uint)(string.IsNullOrEmpty(Language) ? 0 : Language.Length * 2) // Language + (uint)(string.IsNullOrEmpty(Database) ? 0 : Database.Length * 2) // Database + (uint)(string.IsNullOrEmpty(AttachDatabaseFile) ? 0 : AttachDatabaseFile.Length * 2) // AttachDatabaseFile + (uint)(string.IsNullOrEmpty(ChangePassword) ? 0 : ChangePassword.Length * 2) // ChangePassword + (uint)(SSPI == null ? 0 : SSPI.Length) // SSPI + 0); // Feature extension MemoryStream featureExtension = null; // Check if we have a feature extension if (FeatureExt != null) { // Allocate feature extension block featureExtension = new MemoryStream(); // Serialize feature extension FeatureExt.Deflate(featureExtension); // Update total lentgh totalPacketLength += (uint)(sizeof(uint) /* Offset of feature extension data */ + featureExtension.Length /* feature extension itself*/); } // Write packet length TDSUtilities.WriteUInt(destination, totalPacketLength); // Compile TDS version uint tdsVersion = Convert.ToUInt32(string.Format("{0:X}", Math.Max(TDSVersion.Major, 0)) + string.Format("{0:X}", Math.Max(TDSVersion.Minor, 0)) + string.Format("{0:X2}", Math.Max(TDSVersion.Build, 0)) + string.Format("{0:X4}", Math.Max(TDSVersion.Revision, 0)), 16); // Write TDS version TDSUtilities.WriteUInt(destination, tdsVersion); // Write packet length TDSUtilities.WriteUInt(destination, PacketSize); // Write client program version TDSUtilities.WriteUInt(destination, ClientProgramVersion); // Write client program identifier TDSUtilities.WriteUInt(destination, ClientPID); // Write connection identifier TDSUtilities.WriteUInt(destination, ConnectionID); // Write the first optional flags destination.WriteByte(OptionalFlags1.ToByte()); // Write the second optional flags destination.WriteByte(OptionalFlags2.ToByte()); // Instantiate type flags destination.WriteByte(TypeFlags.ToByte()); // Write the third optional flags destination.WriteByte(OptionalFlags3.ToByte()); // Write client time zone TDSUtilities.WriteInt(destination, ClientTimeZone); // Write client locale identifier TDSUtilities.WriteUInt(destination, ClientLCID); // Prepare a collection of property values that will be set later IList <TDSLogin7TokenOffsetProperty> variableProperties = new List <TDSLogin7TokenOffsetProperty>(); // Write client host name variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("HostName"), FixedPacketLength, (ushort)(string.IsNullOrEmpty(HostName) ? 0 : HostName.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write user name and password variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("UserID"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(UserID) ? 0 : UserID.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("Password"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(Password) ? 0 : Password.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write application name variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("ApplicationName"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(ApplicationName) ? 0 : ApplicationName.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write server name variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("ServerName"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(ServerName) ? 0 : ServerName.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Check if we have a feature extension block if (FeatureExt != null) { // Write the offset of the feature extension offset (pointer to pointer) variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("FeatureExt"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), sizeof(uint) / 2, true)); // Should be 4 bytes, devided by 2 because the next guy multiplies by 2 TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)(variableProperties.Last().Length * 2)); // Compensate for division by 2 above } else { // Skip unused TDSUtilities.WriteUShort(destination, 0); TDSUtilities.WriteUShort(destination, 0); } // Write client library name // We do not need to account for skipped unused bytes here because they're already accounted in fixedPacketLength variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("LibraryName"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(LibraryName) ? 0 : LibraryName.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write language variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("Language"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(Language) ? 0 : Language.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write database variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("Database"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(Database) ? 0 : Database.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Check if client is defined if (ClientID == null) { // Allocate empty identifier ClientID = new byte[6]; } // Write unique client identifier destination.Write(ClientID, 0, 6); // Write SSPI variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("SSPI"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(SSPI == null ? 0 : SSPI.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write database file to be attached. NOTE, "variableProperties.Last().Length" without " * 2" because the preceeding buffer isn't string variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("AttachDatabaseFile"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length), (ushort)(string.IsNullOrEmpty(AttachDatabaseFile) ? 0 : AttachDatabaseFile.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Write password change variableProperties.Add(new TDSLogin7TokenOffsetProperty(GetType().GetProperty("ChangePassword"), (ushort)(variableProperties.Last().Position + variableProperties.Last().Length * 2), (ushort)(string.IsNullOrEmpty(ChangePassword) ? 0 : ChangePassword.Length))); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Position); TDSUtilities.WriteUShort(destination, (ushort)variableProperties.Last().Length); // Skip long SSPI TDSUtilities.WriteUInt(destination, 0); // We will be changing collection as we go and serialize everything. As such we can't use foreach and iterator. int iCurrentProperty = 0; // Iterate through the collection while (iCurrentProperty < variableProperties.Count) { // Get current property by index TDSLogin7TokenOffsetProperty property = variableProperties[iCurrentProperty]; // Check if length is positive if (property.Length == 0) { // Move to the next property iCurrentProperty++; continue; } // Check special properties if (property.Property.Name == "Password" || property.Property.Name == "ChangePassword") { // Write encrypted string value TDSUtilities.WritePasswordString(destination, (string)property.Property.GetValue(this, null)); } else if (property.Property.Name == "FeatureExt") { // Check if we are to serialize the offset or the actual data if (property.IsOffsetOffset) { // Property will be written at the offset immediately following all variable length data property.Position = variableProperties.Last().Position + variableProperties.Last().Length; // Write the position at which we'll be serializing the feature extension block TDSUtilities.WriteUInt(destination, property.Position); // Order strings in ascending order by offset variableProperties = variableProperties.OrderBy(p => p.Position).ToList(); // Compensate increment to the next position in order to stay on the same iCurrentProperty--; // No longer offset, actual data is going to follow property.IsOffsetOffset = false; } else { // Transfer deflated feature extension into the login stream featureExtension.WriteTo(destination); } } else if (property.Property.Name == "SSPI") { // Write SSPI destination.Write(SSPI, 0, SSPI.Length); } else { // Write the string value TDSUtilities.WriteString(destination, (string)property.Property.GetValue(this, null)); } // Move to the next property iCurrentProperty++; } }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public override void Deflate(Stream destination) { destination.WriteByte((byte)TDSTokenType.ReturnValue); TDSUtilities.WriteUShort(destination, ParamOrdinal); destination.WriteByte((byte)ParamName.Length); TDSUtilities.WriteString(destination, ParamName); destination.WriteByte((byte)Status); TDSUtilities.WriteUInt(destination, UserType); TDSUtilities.WriteUShort(destination, Flags.ToUShort()); // Write type destination.WriteByte((byte)DataType); // Dispatch further writing based on the type switch (DataType) { case TDSDataType.Binary: case TDSDataType.VarBinary: case TDSDataType.Char: case TDSDataType.VarChar: case TDSDataType.BitN: case TDSDataType.Guid: case TDSDataType.IntN: case TDSDataType.MoneyN: case TDSDataType.FloatN: case TDSDataType.DateTimeN: { // Byte length destination.WriteByte((byte)DataTypeSpecific); break; } case TDSDataType.DateN: { // No details break; } case TDSDataType.TimeN: case TDSDataType.DateTime2N: case TDSDataType.DateTimeOffsetN: { // Scale destination.WriteByte((byte)DataTypeSpecific); break; } case TDSDataType.DecimalN: case TDSDataType.NumericN: { // Cast to type-specific information TDSDecimalColumnSpecific typeSpecific = DataTypeSpecific as TDSDecimalColumnSpecific; // Write values destination.WriteByte(typeSpecific.Length); destination.WriteByte(typeSpecific.Precision); destination.WriteByte(typeSpecific.Scale); break; } case TDSDataType.BigBinary: case TDSDataType.BigVarBinary: { // Short length TDSUtilities.WriteUShort(destination, (ushort)DataTypeSpecific); break; } case TDSDataType.BigChar: case TDSDataType.BigVarChar: case TDSDataType.NChar: case TDSDataType.NVarChar: { // Cast to type specific information TDSShilohVarCharColumnSpecific typedSpecific = DataTypeSpecific as TDSShilohVarCharColumnSpecific; // Write length TDSUtilities.WriteUShort(destination, typedSpecific.Length); // Write collation TDSUtilities.WriteUInt(destination, typedSpecific.Collation.WCID); destination.WriteByte(typedSpecific.Collation.SortID); break; } case TDSDataType.Text: case TDSDataType.NText: { // YukonTextType.Len + YukonTextType.tdsCollationInfo + YukonTextType.cParts // cb = sizeof(LONG) + sizeof(TDSCOLLATION) + sizeof(BYTE); break; } case TDSDataType.Image: { // Integer length TDSUtilities.WriteUInt(destination, (uint)DataTypeSpecific); break; } case TDSDataType.SSVariant: { // Data length TDSUtilities.WriteUInt(destination, (uint)DataTypeSpecific); break; } case TDSDataType.Udt: { // hr = GetUDTColFmt(pvOwner, dwTimeout); break; } case TDSDataType.Xml: { // cb = sizeof(lpColFmt->YukonXmlType.bSchemaPresent); break; } } Microsoft.SqlServer.TDS.Row.TDSRowTokenBase.DeflateTypeVarByte(destination, DataType, Value, DataTypeSpecific); }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public void Deflate(Stream destination) { // Write user type TDSUtilities.WriteUInt(destination, UserType); // Convert flags to value and write them TDSUtilities.WriteUShort(destination, Flags.ToUShort()); // Write type destination.WriteByte((byte)DataType); // Dispatch further writing based on the type switch (DataType) { case TDSDataType.Binary: case TDSDataType.VarBinary: case TDSDataType.Char: case TDSDataType.VarChar: case TDSDataType.BitN: case TDSDataType.Guid: case TDSDataType.IntN: case TDSDataType.MoneyN: case TDSDataType.FloatN: case TDSDataType.DateTimeN: { // Byte length destination.WriteByte((byte)DataTypeSpecific); break; } case TDSDataType.DateN: { // No details break; } case TDSDataType.TimeN: case TDSDataType.DateTime2N: case TDSDataType.DateTimeOffsetN: { // Scale destination.WriteByte((byte)DataTypeSpecific); break; } case TDSDataType.DecimalN: case TDSDataType.NumericN: { // Cast to type-specific information TDSDecimalColumnSpecific typeSpecific = DataTypeSpecific as TDSDecimalColumnSpecific; // Write values destination.WriteByte(typeSpecific.Length); destination.WriteByte(typeSpecific.Precision); destination.WriteByte(typeSpecific.Scale); break; } case TDSDataType.BigBinary: case TDSDataType.BigVarBinary: { // Short length TDSUtilities.WriteUShort(destination, (ushort)DataTypeSpecific); break; } case TDSDataType.BigChar: case TDSDataType.BigVarChar: case TDSDataType.NChar: case TDSDataType.NVarChar: { // Cast to type specific information TDSShilohVarCharColumnSpecific typedSpecific = DataTypeSpecific as TDSShilohVarCharColumnSpecific; // Write length TDSUtilities.WriteUShort(destination, typedSpecific.Length); // Write collation TDSUtilities.WriteUInt(destination, typedSpecific.Collation.WCID); destination.WriteByte(typedSpecific.Collation.SortID); break; } case TDSDataType.Text: case TDSDataType.NText: { // YukonTextType.Len + YukonTextType.tdsCollationInfo + YukonTextType.cParts // cb = sizeof(LONG) + sizeof(TDSCOLLATION) + sizeof(BYTE); break; } case TDSDataType.Image: { // Integer length TDSUtilities.WriteUInt(destination, (uint)DataTypeSpecific); break; } case TDSDataType.SSVariant: { // Data length TDSUtilities.WriteUInt(destination, (uint)DataTypeSpecific); break; } case TDSDataType.Udt: { // hr = GetUDTColFmt(pvOwner, dwTimeout); break; } case TDSDataType.Xml: { // cb = sizeof(lpColFmt->YukonXmlType.bSchemaPresent); destination.WriteByte(0); break; } } // Check if we need to write table name if ((DataType == TDSDataType.Text || DataType == TDSDataType.NText || DataType == TDSDataType.Image) && (TableName != null)) { // Write part count destination.WriteByte((byte)TableName.Count); // Write each part foreach (string part in TableName) { // Write table part length TDSUtilities.WriteUShort(destination, (ushort)(string.IsNullOrEmpty(part) ? 0 : part.Length)); // Write table part TDSUtilities.WriteString(destination, part); } } // Write column name length destination.WriteByte((byte)(string.IsNullOrEmpty(Name) ? 0 : Name.Length)); // Write column name TDSUtilities.WriteString(destination, Name); }
/// <summary> /// Deflate the token /// </summary> /// <param name="destination">Stream to deflate token to</param> public override void Deflate(Stream destination) { // Allocate stream for token data // We need to cache it to calculate the environment change token length MemoryStream cache = new MemoryStream(); // Write environment change type cache.WriteByte((byte)Type); // Write the rest of the token based on the token type switch (Type) { case TDSEnvChangeTokenType.Database: case TDSEnvChangeTokenType.Language: case TDSEnvChangeTokenType.CharacterSet: case TDSEnvChangeTokenType.PacketSize: case TDSEnvChangeTokenType.RealTimeLogShipping: { // Write new value length cache.WriteByte((byte)(string.IsNullOrEmpty((string)NewValue) ? 0 : ((string)NewValue).Length)); // Write new value TDSUtilities.WriteString(cache, (string)NewValue); // Write old value length cache.WriteByte((byte)(string.IsNullOrEmpty((string)OldValue) ? 0 : ((string)OldValue).Length)); // Write old value TDSUtilities.WriteString(cache, (string)OldValue); break; } case TDSEnvChangeTokenType.Routing: { // Create a sub-cache to determine the value length MemoryStream subCache = new MemoryStream(); // Check if new value exists if (NewValue != null) { // Deflate token value (NewValue as TDSRoutingEnvChangeTokenValue).Deflate(subCache); } // Write new value length TDSUtilities.WriteUShort(cache, (ushort)subCache.Length); // Write new value subCache.WriteTo(cache); // Write zero for the old value length TDSUtilities.WriteUShort(cache, 0); break; } case TDSEnvChangeTokenType.SQLCollation: { // Write new value length cache.WriteByte((byte)(NewValue != null ? ((byte[])NewValue).Length : 0)); // Check if we have a new value if (NewValue != null) { // Write new value cache.Write((byte[])NewValue, 0, ((byte[])NewValue).Length); } // Write old value length cache.WriteByte((byte)(OldValue != null ? ((byte[])OldValue).Length : 0)); // Check if we have a old value if (OldValue != null) { // Write old value cache.Write((byte[])OldValue, 0, ((byte[])OldValue).Length); } break; } default: { throw new Exception("Unrecognized environment change type"); } } // Write token identifier destination.WriteByte((byte)TDSTokenType.EnvironmentChange); // Write length TDSUtilities.WriteUShort(destination, (ushort)cache.Length); // Write token data cache.WriteTo(destination); }